General Setup

Setup chunk

Setup reticulate

knitr::opts_chunk$set(fig.width = 8)
knitr::opts_knit$set(root.dir = normalizePath(".."))
knitr::opts_knit$get("root.dir")
[1] "/nas/groups/treutlein/USERS/tomasgomes/projects/liver_regen"

Load libraries

library(reticulate)
knitr::knit_engines$set(python = reticulate::eng_python)
py_available(initialize = FALSE)
[1] FALSE
use_python(Sys.which("python"))
py_config()
Warning: Python 2 reached EOL on January 1, 2020. Python 2 compatability be removed in an upcoming reticulate release.
python:         /usr/bin/python
libpython:      /usr/lib64/python2.7/config/libpython2.7.so
pythonhome:     //usr://usr
version:        2.7.5 (default, May 30 2023, 03:38:55)  [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
numpy:           [NOT FOUND]

NOTE: Python version was forced by use_python function

Load and preprocess data

Load data (from all cells)

library(Seurat)
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Attaching SeuratObject
library(ggplot2)
library(ggridges)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(igraph)

Attaching package: ‘igraph’

The following objects are masked from ‘package:dplyr’:

    as_data_frame, groups, union

The following objects are masked from ‘package:stats’:

    decompose, spectrum

The following object is masked from ‘package:base’:

    union
library(data.table)
data.table 1.14.6 using 72 threads (see ?getDTthreads).  Latest news: r-datatable.com

Attaching package: ‘data.table’

The following objects are masked from ‘package:dplyr’:

    between, first, last

Prepare a global object with all necessary metadata

allcells_css = readRDS(file = "data/processed/allcells_css.RDS")
end_cells = readRDS(file = "results/endothelial/only_end_cells_zon.RDS")
hep_cells = readRDS(file = "results/zonation_cond/hep_cells_zonation_rank.RDS")
imm_cells = readRDS(file = "results/immune/all_imm_cells.RDS")

# mesenchymal annotation
mes_annot = read.csv("./data/processed/mesenchymal_fresh_annot.csv", 
                     row.names = 1, header = T, )

Subset and process each condition

# endothelial
endpop_df = data.frame(row.names = colnames(end_cells),
                       "subpops" = end_cells@meta.data$endo_simp)
# immune
immpop_df = data.frame(row.names = colnames(imm_cells),
                       "subpops" = imm_cells@meta.data$immune_annot)
# hepatocyte
heppop_df = lapply(hep_cells, function(x) cbind(colnames(x), as.character(x$zonation_int)))
heppop_df = Reduce(rbind, heppop_df)
heppop_df = data.frame(row.names = heppop_df[,1],
                       subpops = factor(heppop_df[,2]))
levels(heppop_df$subpops) = c("(-0.00099,0.333]" = "Hepatocytes_Z1",
                              "(0.333,0.667]" = "Hepatocytes_Z2",
                              "(0.667,1]" = "Hepatocytes_Z3")
heppop_df$subpops = as.character(heppop_df$subpops)
# mesenchymal
mes_df = mes_annot[,"annotation", drop = F]
colnames(mes_df) = "subpops"
mes_df$subpops[grepl("doublets",mes_df$subpops)] = "Doublets"

subpop_df = rbind(endpop_df, immpop_df, heppop_df, mes_df)
subpop_df$subpops[subpop_df$subpops=="Cycling cells"] = "Dividing endothelial cells"

# add subpop metadata
allcells_css = AddMetaData(allcells_css, subpop_df)
allcells_css$subpops[is.na(allcells_css$subpops)] = allcells_css$allcells_major[is.na(allcells_css$subpops)]
allcells_css$subpops[allcells_css$subpops=="Hepatocyte-Monocyte interaction"] = "Doublets"

# the cells in this object named "Hepatocytes", "Endothelial cells", and "Doublets" have to be removed
## the first two are cells that didn't pass the hep and end analysis
sub_allcells_css = allcells_css[,!(allcells_css$subpops %in% c("Hepatocytes", "Endothelial cells",
                                                               "Doublets"))]

# add simplified column - merge some populations, don't include cycling pops and stressed T cells
sub_allcells_css$subpops_simp = sub_allcells_css$subpops
sub_allcells_css$subpops_simp[grepl("MAIT", sub_allcells_css$subpops_simp)] = "MAIT cells"
sub_allcells_css$subpops_simp[grepl("NK cells ", sub_allcells_css$subpops_simp)] = "NK cells"
sub_allcells_css$subpops_simp[grepl("LSEC (high MT", sub_allcells_css$subpops_simp, 
                                    fixed = T)] = "LSEC (high MT)"
sub_allcells_css$subpops_simp[grepl("CD8 ab-T ", sub_allcells_css$subpops_simp, 
                                    fixed = T)] = "CD8 ab-T cells"
simp_allcells_css = sub_allcells_css[,!(sub_allcells_css$subpops_simp %in% c("ab-T cells (stress)", "Dividing cDCs", "Dividing endothelial cells","Dividing T/NK cells"))]

Running CellPhoneDB

Commands for runnin CellPhoneDB will be written here. Results are output to the local1 disk to avoid I/O issues, but the output folders should then be copied to: results/cell_comm_healthy/CellPhoneDB_runs.
NOTE: for some reason I’m getting a SegFault when trying to run the heatmap_plot command. This was run on the Euler server instead.

#cpdb_path = "results/cell_comm/CellPhoneDB_V3"
cpdb_path = "/local1/scratch/tomasgomes/CellPhoneDB_V3"

cond_cells = list()
for(cond in unique(sub_allcells_css@meta.data$Condition)){
  if(!dir.exists(paste0(cpdb_path, "/", cond, "/"))){
    dir.create(paste0(cpdb_path, "/", cond, "/"))
  }
  # subset condition
  cond_cells[[cond]] = sub_allcells_css[,sub_allcells_css@meta.data$Condition==cond]
  
  # define metadata
  meta = data.frame(row.names = rownames(cond_cells[[cond]]@meta.data),
                    "Cell" = rownames(cond_cells[[cond]]@meta.data), 
                    "cell_type" = as.character(cond_cells[[cond]]@meta.data$subpops),
                    stringsAsFactors = F)
  write.table(meta, file = paste0(cpdb_path, "/", cond, "/", cond, "_meta_names.txt"), 
            sep = "\t", col.names = T, row.names = F, quote = F)
  
  # normalise and save
  cond_cells[[cond]] = suppressWarnings(SCTransform(cond_cells[[cond]], do.correct.umi = T, 
                                                    vars.to.regress=c("unique_name", "nCount_RNA"),
                                                    variable.features.rv.th = 1, seed.use = 1,
                                                    return.only.var.genes = F, verbose = F,
                                                    variable.features.n = NULL))
  
  dat = cbind(rownames(cond_cells[[cond]]@assays$SCT@data),
              Matrix::as.matrix(cond_cells[[cond]]@assays$SCT@data))
  colnames(dat)[1] = "Gene"
  write.table(dat, file = paste0(cpdb_path, "/", cond, "/", cond, "_exp_norm.txt"), 
              sep = "\t", col.names = T, row.names = F, quote = F)
  
  
  if(!dir.exists(paste0(cpdb_path, "/", cond, "_simp/"))){
    dir.create(paste0(cpdb_path, "/", cond, "_simp/"))
  }
  # subset condition
  cond_cells[[cond]] = simp_allcells_css[,simp_allcells_css@meta.data$Condition==cond]
  
  # define metadata
  meta_simp = data.frame(row.names = rownames(cond_cells[[cond]]@meta.data),
                         "Cell" = rownames(cond_cells[[cond]]@meta.data), 
                         "cell_type" = as.character(cond_cells[[cond]]@meta.data$subpops_simp),
                         stringsAsFactors = F)
  write.table(meta_simp, file = paste0(cpdb_path, "/", cond, "_simp/", cond, "_meta_names.txt"), 
            sep = "\t", col.names = T, row.names = F, quote = F)
   
  # normalise and save
  cond_cells[[cond]] = suppressWarnings(SCTransform(cond_cells[[cond]], do.correct.umi = T, 
                                                    vars.to.regress=c("unique_name", "nCount_RNA"),
                                                    variable.features.rv.th = 1, seed.use = 1,
                                                    return.only.var.genes = F, verbose = F,
                                                    variable.features.n = NULL))
  
  dat = cbind(rownames(cond_cells[[cond]]@assays$SCT@data),
              Matrix::as.matrix(cond_cells[[cond]]@assays$SCT@data))
  colnames(dat)[1] = "Gene"
  write.table(dat, file = paste0(cpdb_path, "/", cond, "_simp/", cond, "_exp_norm.txt"), 
              sep = "\t", col.names = T, row.names = F, quote = F)
}
Warning: sparse->dense coercion: allocating vector of size 3.3 GiBWarning: sparse->dense coercion: allocating vector of size 3.2 GiBWarning: sparse->dense coercion: allocating vector of size 8.8 GiBWarning: sparse->dense coercion: allocating vector of size 8.7 GiBWarning: sparse->dense coercion: allocating vector of size 7.8 GiBWarning: sparse->dense coercion: allocating vector of size 7.7 GiB
for(n in names(cond_cells)){
  comm1 = paste0("cellphonedb method statistical_analysis /local1/scratch/tomasgomes/CellPhoneDB_V3/", n, "/", n, "_meta_names.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/", n, "/", n, "_exp_norm.txt --threads=12 --output-path=/local1/scratch/tomasgomes/liver/CellPhoneDB_V3/", n, " --project-name ", n, " --counts-data gene_name")
  comm2 = paste0("cp -r /local1/scratch/tomasgomes/liver/CellPhoneDB_V3/", n, "/", n, " results/cell_comm/CellPhoneDB_V3/")
  comm3 = paste0("cp -r /local1/scratch/tomasgomes/CellPhoneDB_V3/", n, "/", n, "_meta_names.txt results/cell_comm/CellPhoneDB_V3/", n, "/")
  comm4 = paste0("cellphonedb plot heatmap_plot --pvalues-path ./results/cell_comm/CellPhoneDB_V3/", n, "/pvalues.txt --output-path ./results/cell_comm/CellPhoneDB_V3/", n, "/ --count-name counts_heat.pdf --count-network-name count_net.txt --interaction-count-name count_inter.txt results/cell_comm/CellPhoneDB_V3/", n, "/", n, "_meta_names.txt")
  
  print(n)
  print(comm1)
  print(comm2)
  print(comm3)
  print(comm4)
  print(".")
}
[1] "healthy"
[1] "cellphonedb method statistical_analysis /local1/scratch/tomasgomes/CellPhoneDB_V3/healthy/healthy_meta_names.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/healthy/healthy_exp_norm.txt --threads=12 --output-path=/local1/scratch/tomasgomes/liver/CellPhoneDB_V3/healthy --project-name healthy --counts-data gene_name"
[1] "cp -r /local1/scratch/tomasgomes/liver/CellPhoneDB_V3/healthy/healthy results/cell_comm/CellPhoneDB_V3/"
[1] "cp -r /local1/scratch/tomasgomes/CellPhoneDB_V3/healthy/healthy_meta_names.txt results/cell_comm/CellPhoneDB_V3/healthy/"
[1] "cellphonedb plot heatmap_plot --pvalues-path ./results/cell_comm/CellPhoneDB_V3/healthy/pvalues.txt --output-path ./results/cell_comm/CellPhoneDB_V3/healthy/ --count-name counts_heat.pdf --count-network-name count_net.txt --interaction-count-name count_inter.txt results/cell_comm/CellPhoneDB_V3/healthy/healthy_meta_names.txt"
[1] "."
[1] "embolised"
[1] "cellphonedb method statistical_analysis /local1/scratch/tomasgomes/CellPhoneDB_V3/embolised/embolised_meta_names.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/embolised/embolised_exp_norm.txt --threads=12 --output-path=/local1/scratch/tomasgomes/liver/CellPhoneDB_V3/embolised --project-name embolised --counts-data gene_name"
[1] "cp -r /local1/scratch/tomasgomes/liver/CellPhoneDB_V3/embolised/embolised results/cell_comm/CellPhoneDB_V3/"
[1] "cp -r /local1/scratch/tomasgomes/CellPhoneDB_V3/embolised/embolised_meta_names.txt results/cell_comm/CellPhoneDB_V3/embolised/"
[1] "cellphonedb plot heatmap_plot --pvalues-path ./results/cell_comm/CellPhoneDB_V3/embolised/pvalues.txt --output-path ./results/cell_comm/CellPhoneDB_V3/embolised/ --count-name counts_heat.pdf --count-network-name count_net.txt --interaction-count-name count_inter.txt results/cell_comm/CellPhoneDB_V3/embolised/embolised_meta_names.txt"
[1] "."
[1] "regenerating"
[1] "cellphonedb method statistical_analysis /local1/scratch/tomasgomes/CellPhoneDB_V3/regenerating/regenerating_meta_names.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/regenerating/regenerating_exp_norm.txt --threads=12 --output-path=/local1/scratch/tomasgomes/liver/CellPhoneDB_V3/regenerating --project-name regenerating --counts-data gene_name"
[1] "cp -r /local1/scratch/tomasgomes/liver/CellPhoneDB_V3/regenerating/regenerating results/cell_comm/CellPhoneDB_V3/"
[1] "cp -r /local1/scratch/tomasgomes/CellPhoneDB_V3/regenerating/regenerating_meta_names.txt results/cell_comm/CellPhoneDB_V3/regenerating/"
[1] "cellphonedb plot heatmap_plot --pvalues-path ./results/cell_comm/CellPhoneDB_V3/regenerating/pvalues.txt --output-path ./results/cell_comm/CellPhoneDB_V3/regenerating/ --count-name counts_heat.pdf --count-network-name count_net.txt --interaction-count-name count_inter.txt results/cell_comm/CellPhoneDB_V3/regenerating/regenerating_meta_names.txt"
[1] "."

Process results

Load results

for(n in names(cond_cells)){
  comm1 = paste0("cellphonedb method statistical_analysis /local1/scratch/tomasgomes/CellPhoneDB_V3/", n, "_simp/", n, "_meta_names.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/", n, "_simp/", n, "_exp_norm.txt --threads=12 --output-path=/local1/scratch/tomasgomes/liver/CellPhoneDB_V3/", n, "_simp --project-name ", n, "_simp --counts-data gene_name")
  comm2 = paste0("cp -r /local1/scratch/tomasgomes/liver/CellPhoneDB_V3/", n, "_simp/", n, "_simp results/cell_comm/CellPhoneDB_V3/")
  comm3 = paste0("cp -r /local1/scratch/tomasgomes/CellPhoneDB_V3/", n, "_simp/", n, "_meta_names.txt results/cell_comm/CellPhoneDB_V3/", n, "_simp/")
  comm4 = paste0("cellphonedb plot heatmap_plot --pvalues-path ./results/cell_comm/CellPhoneDB_V3/", n, "_simp/pvalues.txt --output-path ./results/cell_comm/CellPhoneDB_V3/", n, "_simp/ --count-name counts_heat.pdf --count-network-name count_net.txt --interaction-count-name count_inter.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/", n, "_simp/", n, "_meta_names.txt")
  
  print(n)
  print(comm1)
  print(comm2)
  print(comm3)
  print(comm4)
  print(".")
}
[1] "healthy"
[1] "cellphonedb method statistical_analysis /local1/scratch/tomasgomes/CellPhoneDB_V3/healthy_simp/healthy_meta_names.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/healthy_simp/healthy_exp_norm.txt --threads=12 --output-path=/local1/scratch/tomasgomes/liver/CellPhoneDB_V3/healthy_simp --project-name healthy_simp --counts-data gene_name"
[1] "cp -r /local1/scratch/tomasgomes/liver/CellPhoneDB_V3/healthy_simp/healthy_simp results/cell_comm/CellPhoneDB_V3/"
[1] "cp -r /local1/scratch/tomasgomes/CellPhoneDB_V3/healthy_simp/healthy_meta_names.txt results/cell_comm/CellPhoneDB_V3/healthy_simp/"
[1] "cellphonedb plot heatmap_plot --pvalues-path ./results/cell_comm/CellPhoneDB_V3/healthy_simp/pvalues.txt --output-path ./results/cell_comm/CellPhoneDB_V3/healthy_simp/ --count-name counts_heat.pdf --count-network-name count_net.txt --interaction-count-name count_inter.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/healthy_simp/healthy_meta_names.txt"
[1] "."
[1] "embolised"
[1] "cellphonedb method statistical_analysis /local1/scratch/tomasgomes/CellPhoneDB_V3/embolised_simp/embolised_meta_names.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/embolised_simp/embolised_exp_norm.txt --threads=12 --output-path=/local1/scratch/tomasgomes/liver/CellPhoneDB_V3/embolised_simp --project-name embolised_simp --counts-data gene_name"
[1] "cp -r /local1/scratch/tomasgomes/liver/CellPhoneDB_V3/embolised_simp/embolised_simp results/cell_comm/CellPhoneDB_V3/"
[1] "cp -r /local1/scratch/tomasgomes/CellPhoneDB_V3/embolised_simp/embolised_meta_names.txt results/cell_comm/CellPhoneDB_V3/embolised_simp/"
[1] "cellphonedb plot heatmap_plot --pvalues-path ./results/cell_comm/CellPhoneDB_V3/embolised_simp/pvalues.txt --output-path ./results/cell_comm/CellPhoneDB_V3/embolised_simp/ --count-name counts_heat.pdf --count-network-name count_net.txt --interaction-count-name count_inter.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/embolised_simp/embolised_meta_names.txt"
[1] "."
[1] "regenerating"
[1] "cellphonedb method statistical_analysis /local1/scratch/tomasgomes/CellPhoneDB_V3/regenerating_simp/regenerating_meta_names.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/regenerating_simp/regenerating_exp_norm.txt --threads=12 --output-path=/local1/scratch/tomasgomes/liver/CellPhoneDB_V3/regenerating_simp --project-name regenerating_simp --counts-data gene_name"
[1] "cp -r /local1/scratch/tomasgomes/liver/CellPhoneDB_V3/regenerating_simp/regenerating_simp results/cell_comm/CellPhoneDB_V3/"
[1] "cp -r /local1/scratch/tomasgomes/CellPhoneDB_V3/regenerating_simp/regenerating_meta_names.txt results/cell_comm/CellPhoneDB_V3/regenerating_simp/"
[1] "cellphonedb plot heatmap_plot --pvalues-path ./results/cell_comm/CellPhoneDB_V3/regenerating_simp/pvalues.txt --output-path ./results/cell_comm/CellPhoneDB_V3/regenerating_simp/ --count-name counts_heat.pdf --count-network-name count_net.txt --interaction-count-name count_inter.txt /local1/scratch/tomasgomes/CellPhoneDB_V3/regenerating_simp/regenerating_meta_names.txt"
[1] "."

Reformat significant means matrix

cpdb_path = "results/cell_comm/CellPhoneDB_V3"

sig_means_names_l = list()
dec_l = list()
net_names_l = list()
meta_l = list()
conds = unique(allcells_css@meta.data$Condition)
for(n in c(conds, paste0(conds, "_simp"))){
  ns = strsplit(n, "_")[[1]][1]
  sig_means_names_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/significant_means.txt"),
                                      header = T, sep = "\t", stringsAsFactors = F)
  dec_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/deconvoluted.txt"),
                          header = T, sep = "\t")
  colnames(dec_l[[n]]) = gsub(".", " ", colnames(dec_l[[n]]), fixed = T)
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="gd T cells"] = "gd-T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Macrophages  HES4  "] = "Macrophages (HES4+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells"] = "CD8 ab-T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 1"] = "CD8 ab-T cells 1"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 2"] = "CD8 ab-T cells 2"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 3"] = "CD8 ab-T cells 3"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Naive CD4  T cells"] = "Naive CD4+ T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="ab T cells  stress "] = "ab-T cells (stress)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  secretory "] = "Monocytes (secretory)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  TREM2  CD9  "] = "Monocytes (TREM2+ CD9+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  IGSF21  GPR34  "] = "Monocytes (IGSF21+ GPR34+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  stress "] = "LSEC (stress)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  remodelling "] = "LSEC (remodelling)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  interferon "] = "LSEC (interferon)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT 2 "] = "LSEC (high MT 2)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT 1 "] = "LSEC (high MT 1)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT "] = "LSEC (high MT)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  fenestr  "] = "LSEC (fenestr.)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Kupffer cells  SUCNR1  "] = "Kupffer cells (SUCNR1+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="IgG  Plasma cells"] = "IgG+ Plasma cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="IgA  Plasma cells"] = "IgA+ Plasma cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells  stress "] = "CD8 ab-T cells (stress)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="EC non LSEC"] = "EC non-LSEC"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Dividing T NK cells"] = "Dividing T/NK cells"
  net_names_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/count_net.txt"),
                                header = T, sep = "\t", stringsAsFactors = F)
  meta_l[[n]] = if(!grepl("simp", n)){
    read.table(paste0(cpdb_path, "/", n, "/", n, "_meta_names.txt"),header = T,sep = "\t")
  } else{
    read.table(paste0(cpdb_path, "/", n, "/", ns, "_meta_names.txt"),header = T,sep = "\t")
  }
}
saveRDS(dec_l, file = "results/cell_comm/v3/deconvoluted_list.RDS")
saveRDS(net_names_l, file = "results/cell_comm/v3/count_net_list.RDS")

Plot interaction counts

reform_list = list()
for(n in names(meta_l)){
  simp_reform = reshape2::melt(sig_means_names_l[[n]][,c(1:4,7:9,13:ncol(sig_means_names_l[[n]]))])
  simp_reform = simp_reform[complete.cases(simp_reform),]
  
  lr1 = c()
  lr2 = c()
  for(i in 1:nrow(simp_reform)){
    if(grepl("complex", simp_reform$partner_a[i])){
      lr1 = c(lr1, substr(simp_reform$partner_a[i], 9,100))
    } else{
      lr1 = c(lr1, strsplit(simp_reform$interacting_pair[i], "_")[[1]][1])
    }
    if(grepl("complex", simp_reform$partner_b[i])){
      lr2 = c(lr2, substr(simp_reform$partner_b[i], 9,100))
    } else{
      spltlen = length(strsplit(simp_reform$interacting_pair[i], "_")[[1]])
      lr2 = c(lr2, strsplit(simp_reform$interacting_pair[i], "_")[[1]][spltlen])
    }
  }
  simp_reform$lr1 = lr1
  simp_reform$lr2 = lr2
  
  simp_reform$ct1 = NA
  simp_reform$ct2 = NA
  donest = rep(NA, length(simp_reform$ct1))
  doneen = rep(NA, length(simp_reform$ct2))
  for(ct in sort(unique(meta_l[[n]]$cell_type), decreasing = T)){
    ct_mod = gsub("-", " ", ct, fixed = T)
    ct_mod = gsub("+", " ", ct_mod, fixed = T)
    ct_mod = gsub("/", " ", ct_mod, fixed = T)
    ct_mod = gsub("(", " ", ct_mod, fixed = T)
    ct_mod = gsub(")", " ", ct_mod, fixed = T)
    ct_mod = gsub(".", " ", ct_mod, fixed = T)
    st = grepl(paste0("^",ct_mod, " "), gsub(".", " ", simp_reform$variable, fixed = T), fixed = F)
    en = grepl(paste0(" ",ct_mod,"$"), gsub(".", " ", simp_reform$variable, fixed = T), fixed = F)
    simp_reform$ct1[st & is.na(donest)] = ct
    simp_reform$ct2[en & is.na(doneen)] = ct
    donest[st] = T
    doneen[en] = T
  }
  #colnames(dec_l[[n]])[!(colnames(dec_l[[n]]) %in% unique(simp_reform$ct2))]
  
  # direction: receptors activate internal signal - that is the direction of the signalling
  ## if both are true, the "net signal" is 0
  simp_reform$dir = ifelse(simp_reform$receptor_a==simp_reform$receptor_b, 0, 
                           ifelse(simp_reform$receptor_a=="True" & 
                                    simp_reform$receptor_b=="False", -1,
                                  ifelse(simp_reform$receptor_a=="False" &
                                           simp_reform$receptor_b=="True", 1, NA)))
  
  simp_reform = simp_reform[,c("id_cp_interaction", "ct1", "ct2", "lr1", "lr2", 
                               "value", "dir")]
  
  for(i in 1:nrow(simp_reform)){
    if(simp_reform[i,"dir"]==-1){
      tmp = simp_reform[i,"ct2"]
      simp_reform[i,"ct2"] = simp_reform[i,"ct1"]
      simp_reform[i,"ct1"] = tmp
      
      tmp = simp_reform[i,"lr2"]
      simp_reform[i,"lr2"] = simp_reform[i,"lr1"]
      simp_reform[i,"lr1"] = tmp
      
      simp_reform[i,"dir"]=1
    }
  }
  
 reform_list[[n]] = simp_reform
}
Using id_cp_interaction, interacting_pair, partner_a, partner_b, secreted, receptor_a, receptor_b as id variables
Using id_cp_interaction, interacting_pair, partner_a, partner_b, secreted, receptor_a, receptor_b as id variables
Using id_cp_interaction, interacting_pair, partner_a, partner_b, secreted, receptor_a, receptor_b as id variables
Using id_cp_interaction, interacting_pair, partner_a, partner_b, secreted, receptor_a, receptor_b as id variables
Using id_cp_interaction, interacting_pair, partner_a, partner_b, secreted, receptor_a, receptor_b as id variables
Using id_cp_interaction, interacting_pair, partner_a, partner_b, secreted, receptor_a, receptor_b as id variables
saveRDS(reform_list, file = "results/cell_comm/v3/reform_list.RDS")

Making two cell type by interaction matrices: one has the ligand mean, another the receptor mean

inter_counts_l = list()
for(n in names(net_names_l)){
  inter_counts = reshape2::dcast(data = net_names_l[[n]], 
                                 formula = SOURCE~TARGET, value.var = "count")
  rownames(inter_counts) = inter_counts[,1]
  inter_counts = inter_counts[,-1]
  
  pdf(paste0("results/cell_comm/v3/", n, "_number_of_interactions.pdf"), useDingbats = F, 
      height = 10, width = 10)
  pheatmap::pheatmap(inter_counts, clustering_method = "ward.D2", main = "All clusters")
  dev.off()
  inter_counts_l[[n]] = inter_counts
}

Making two cell type by interaction matrices: one has the ligand mean, another the receptor mean (here only directional)

scoring_mats = list()
for(n in names(reform_list)){
  cl_reform = reform_list[[n]]
  # add reversed non-directed interactions, to consider them in both directions
  cl_reform0 = cl_reform[cl_reform$dir==0,]
  cl_reform0 = cl_reform0[,c(1,3,2,5,4,6,7)]
  colnames(cl_reform0) = colnames(cl_reform)
  cl_reform = rbind(cl_reform, cl_reform0)
  
  dec_cl = dec_l[[n]]
  
  mat_lig_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  mat_rec_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  colnames(mat_lig_cl) = colnames(mat_rec_cl) = unique(cl_reform$ct1)
  rownames(mat_lig_cl) = rownames(mat_rec_cl) = unique(cl_reform$id_cp_interaction)
  for(i in 1:nrow(cl_reform)){
    g1 = cl_reform[i,"lr1"]
    g2 = cl_reform[i,"lr2"]
    
    c1 = cl_reform[i,"ct1"]
    c2 = cl_reform[i,"ct2"]
    
    int = as.character(cl_reform[i,1])
    
    m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g1,c1], na.rm = T)
    if(is.na(m1)) m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g1,c1], 
                            na.rm = T)
    mat_lig_cl[int, c1] = m1
    
    m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g2,c2], na.rm = T)
    if(is.na(m2)) m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g2,c2],
                            na.rm = T)
    if(is.na(m1) | is.na(m2)) print(int)
    mat_rec_cl[int, c2] = m2
  }
  
  # ligand vs receptor correlation
  cor_mat_each_cl = cor(mat_lig_cl, mat_rec_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats[[n]] = list("mat_lig_ct" = mat_lig_cl, "mat_rec_ct" = mat_rec_cl,
                           "cor_mat_each" = cor_mat_each_cl)
  
  # sum(lig, reg) correlation
  mat_lig_cl[is.na(mat_lig_cl)] = 0
  mat_rec_cl[is.na(mat_rec_cl)] = 0
  mat_both_cl = mat_lig_cl+mat_rec_cl
  mat_both_cl[mat_both_cl==0] = NA
  cor_mat_both_cl = cor(mat_both_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats[[n]]$cor_mat_both = cor_mat_both_cl
}
saveRDS(scoring_mats, file = "results/cell_comm/v3/scoring_mats.RDS")

Count interactions of each type

scoring_mats_dir = list()
for(n in names(reform_list)){
  cl_reform = reform_list[[n]]
  # add reversed non-directed interactions, to consider them in both directions
  cl_reform0 = cl_reform[cl_reform$dir==0,]
  cl_reform0 = cl_reform0[,c(1,3,2,5,4,6,7)]
  colnames(cl_reform0) = colnames(cl_reform)
  cl_reform = rbind(cl_reform, cl_reform0)
  
  cl_reform = cl_reform[cl_reform$dir==1,]
  
  dec_cl = dec_l[[n]]
  
  mat_lig_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  mat_rec_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  colnames(mat_lig_cl) = colnames(mat_rec_cl) = unique(cl_reform$ct1)
  rownames(mat_lig_cl) = rownames(mat_rec_cl) = unique(cl_reform$id_cp_interaction)
  for(i in 1:nrow(cl_reform)){
    g1 = cl_reform[i,"lr1"]
    g2 = cl_reform[i,"lr2"]
    
    c1 = cl_reform[i,"ct1"]
    c2 = cl_reform[i,"ct2"]
    
    int = as.character(cl_reform[i,1])
    
    m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g1,c1], na.rm = T)
    if(is.na(m1)) m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g1,c1], 
                            na.rm = T)
    mat_lig_cl[int, c1] = m1
    
    m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g2,c2], na.rm = T)
    if(is.na(m2)) m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g2,c2],
                            na.rm = T)
    if(is.na(m1) | is.na(m2)) print(int)
    mat_rec_cl[int, c2] = m2
  }
  
  # ligand vs receptor correlation
  cor_mat_each_cl = cor(mat_lig_cl, mat_rec_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats_dir[[n]] = list("mat_lig_ct" = mat_lig_cl, "mat_rec_ct" = mat_rec_cl,
                               "cor_mat_each" = cor_mat_each_cl)
  
  # sum(lig, reg) correlation
  mat_lig_cl[is.na(mat_lig_cl)] = 0
  mat_rec_cl[is.na(mat_rec_cl)] = 0
  mat_both_cl = mat_lig_cl+mat_rec_cl
  mat_both_cl[mat_both_cl==0] = NA
  cor_mat_both_cl = cor(mat_both_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats_dir[[n]]$cor_mat_both = cor_mat_both_cl
}
Warning: the standard deviation is zero
saveRDS(scoring_mats_dir, file = "results/cell_comm/v3/scoring_mats_dir.RDS")

Interaction analysis in conditions

Compare interactions with DE genes

inter_counts_dir = list()
for(n in names(reform_list)){
  inter_counts_dir[[n]] = list()
  for(i in c(0,1)){
    cl_reform = reform_list[[n]]
    cl_reform = cl_reform[cl_reform$dir==i,]
    
    n_dir_int = matrix(0, nrow = length(unique(cl_reform$ct1)),
                       ncol = length(unique(cl_reform$ct1)))
    rownames(n_dir_int) = colnames(n_dir_int) = unique(cl_reform$ct1)
    for(c1 in unique(cl_reform$ct1)){
      for(c2 in unique(cl_reform$ct1)){
        n_dir_int[c1,c2] = nrow(cl_reform[cl_reform$ct1==c1 & cl_reform$ct2==c2,])
        if(i==0){
          n_dir_int[c1,c2] = n_dir_int[c1,c2]+nrow(cl_reform[cl_reform$ct1==c2 &
                                                               cl_reform$ct2==c1,])
        }
      }
    }
    inter_counts_dir[[n]][[as.character(i)]] = n_dir_int
  }
}
saveRDS(inter_counts_dir, file = "results/cell_comm/v3/inter_counts_dir.RDS")

Plot interaction heatmap - total and filtered

# add genes from complexes
complex_ref = unique(rbind(dec_l$healthy[dec_l$healthy$is_complex=="True",c(1,5)], 
                           dec_l$embolised[dec_l$embolised$is_complex=="True",c(1,5)],
                           dec_l$regenerating[dec_l$regenerating$is_complex=="True",c(1,5)],
                           dec_l$healthy_simp[dec_l$healthy_simp$is_complex=="True",c(1,5)],
                           dec_l$embolised_simp[dec_l$embolised_simp$is_complex=="True",c(1,5)],
                           dec_l$regenerating_simp[dec_l$regenerating_simp$is_complex=="True",c(1,5)]))
inter_df = list()
for(n in names(reform_list)){
  tmp = merge(reform_list[[n]], complex_ref, by.x = 4, by.y = 2, all.x = T)[,c(2,3,4,1,5,8,6,7)]
  inter_df[[n]] = merge(tmp, complex_ref, by.x = 5, by.y = 2, all.x = T)[,c(2,3,4,5,6,1,9,7,8)]
  inter_df[[n]]$gene_name.x[is.na(inter_df[[n]]$gene_name.x)] = inter_df[[n]]$lr1[is.na(inter_df[[n]]$gene_name.x)]
  inter_df[[n]]$gene_name.y[is.na(inter_df[[n]]$gene_name.y)] = inter_df[[n]]$lr2[is.na(inter_df[[n]]$gene_name.y)]
  colnames(inter_df[[n]])[c(5,7)] = c("gn1", "gn2")
}

for(cc in names(inter_df)){
  # interaction unique in condition
  unique_inter = setdiff(unique(inter_df[[cc]]$id_cp_interaction),
                         unique(c(inter_df[[names(inter_df)[names(inter_df)!=cc][1]]]$id_cp_interaction,
                                  inter_df[[names(inter_df)[names(inter_df)!=cc][2]]]$id_cp_interaction)))
  inter_df[[cc]]$cpdb_unique = inter_df[[cc]]$id_cp_interaction %in% unique_inter
  
  # lr1 unique in condition
  unique_lr1 = setdiff(unique(inter_df[[cc]]$lr1),
                         unique(c(inter_df[[names(inter_df)[names(inter_df)!=cc][1]]]$lr1,
                                  inter_df[[names(inter_df)[names(inter_df)!=cc][2]]]$lr1)))
  inter_df[[cc]]$cpdb_unique_lr1 = inter_df[[cc]]$lr1 %in% unique_lr1
  
  # lr2 unique in condition
  unique_lr2 = setdiff(unique(inter_df[[cc]]$lr2),
                         unique(c(inter_df[[names(inter_df)[names(inter_df)!=cc][1]]]$lr2,
                                  inter_df[[names(inter_df)[names(inter_df)!=cc][2]]]$lr2)))
  inter_df[[cc]]$cpdb_unique_lr2 = inter_df[[cc]]$lr2 %in% unique_lr2
}
for(cc in names(inter_df)){
  oc1 = names(inter_df)[names(inter_df)!=cc][1]
  oc2 = names(inter_df)[names(inter_df)!=cc][2]
  int1 = paste0(inter_df[[cc]]$id_cp_interaction,inter_df[[cc]]$ct1,inter_df[[cc]]$ct2)
  int2 = unique(paste0(inter_df[[oc1]]$id_cp_interaction,inter_df[[oc1]]$ct1,inter_df[[oc1]]$ct2))
  int3 = unique(paste0(inter_df[[oc2]]$id_cp_interaction,inter_df[[oc2]]$ct1,inter_df[[oc2]]$ct2))
  unique_inter = setdiff(unique(int1), unique(c(int2, int3)))
  inter_df[[cc]]$cpdb_unique_ct = int1 %in% unique_inter
}
for(n in names(inter_df)){
  df = inter_df[[n]]
  df = unique(df[,c(1:3)])
  
  ints_un_ct = rowSums(table(df$id_cp_interaction, df$ct1)>0)<4 | 
    rowSums(table(df$id_cp_interaction, df$ct2)>0)<4
  
  inter_df[[n]]$inter_ct_spec = inter_df[[n]]$id_cp_interaction %in% names(ints_un_ct)[ints_un_ct]
}
for(n in names(inter_df)){
  xxx = data.frame(ct = c(inter_df[[n]][,2], inter_df[[n]][,3]), 
                   lr = c(inter_df[[n]][,4], inter_df[[n]][,6]))
  xxx = unique(xxx)
  tab_lr = (table(xxx$lr, xxx$ct)>0)*1
  tab_lr = rowSums(tab_lr)
  
  inter_df[[n]]$lr1_nct = tab_lr[inter_df[[n]]$lr1]
  inter_df[[n]]$lr2_nct = tab_lr[inter_df[[n]]$lr2]
}
saveRDS(inter_df, file = "results/cell_comm/v3/cond_diff_interact_DE.RDS")

Get non-ubiquitous interactions

int_counts_total = list()
int_counts_filt = list()
for(n in names(inter_df)){
  subdfct = unique(inter_df[[n]][,1:3])
  subdfct = unique(subdfct)
  dfct = data.frame("ct1" = c(subdfct$ct1, subdfct$ct2),
                    "ct2" = c(subdfct$ct2, subdfct$ct1))
  int_counts_total[[n]] = dfct
  pheatmap::pheatmap(table(dfct$ct1, dfct$ct2), main = n)
  
  keep = inter_df[[n]]$lr1_nct<=1 | inter_df[[n]]$lr2_nct<=1
  subdfct = unique(inter_df[[n]][keep,c(1:3, 15,16)])
  swp = subdfct[,5]==1
  tmp = subdfct$ct2[swp]
  subdfct$ct2[swp] = subdfct$ct1[swp]
  subdfct$ct1[swp] = tmp
  tmp = subdfct$lr2_nct[swp]
  subdfct$lr2_nct[swp] = subdfct$lr1_nct[swp]
  subdfct$lr1_nct[swp] = tmp
  dup = subdfct[subdfct$lr1_nct==1 & subdfct$lr2_nct==1,]
  tmp = dup$ct2
  dup$ct2 = dup$ct1
  dup$ct1 = tmp
  subdfct = rbind(subdfct, dup)
  subdfct = unique(subdfct[,1:3])
  int_counts_filt[[n]] = dfct
  pheatmap::pheatmap(table(subdfct$ct1, subdfct$ct2), main = n)
}

saveRDS(int_counts_total, file = "results/cell_comm/v3/int_counts_total.RDS")
saveRDS(int_counts_filt, file = "results/cell_comm/v3/int_counts_filt.RDS")

Annotate interactions

inter_nonss = setdiff(unique(c(inter_df$embolised$id_cp_interaction,
                               inter_df$regenerating$id_cp_interaction)),
                      unique(inter_df$healthy$id_cp_interaction))
inter_nonemb = setdiff(unique(c(inter_df$healthy$id_cp_interaction,
                               inter_df$regenerating$id_cp_interaction)),
                      unique(inter_df$embolised$id_cp_interaction))
inter_nonreg = setdiff(unique(c(inter_df$embolised$id_cp_interaction,
                               inter_df$healthy$id_cp_interaction)),
                      unique(inter_df$regenerating$id_cp_interaction))

ct_g_cond = list()
for(n in names(inter_df)[1:3]){
  df = inter_df[[n]][inter_df[[n]]$cpdb_unique | 
                       inter_df[[n]]$id_cp_interaction %in% c(inter_nonss, inter_nonemb, inter_nonreg),]
  #df = df[df$lr1_nct<=1 | df$lr2_nct<=1,]
  
  cts = unique(c(df$ct1, df$ct2))
  ct_g_list = list()
  for(ct in cts){
    ct_g_list[[ct]] = data.frame("interact" = c(as.character(df$id_cp_interaction[df$ct1==ct]),
                                                as.character(df$id_cp_interaction[df$ct2==ct])),
                                 "gene" = c(as.character(df$gn1[df$ct1==ct]),
                                            as.character(df$gn2[df$ct2==ct])),
                                 "gene_target" = c(as.character(df$gn2[df$ct1==ct]),
                                                   as.character(df$gn1[df$ct2==ct])),
                                 "ct" = ct,
                                 "ct_target" = c(as.character(df$ct2[df$ct1==ct]),
                                                as.character(df$ct1[df$ct2==ct])),
                                 "cond" = n, stringsAsFactors = F)
  }
  ct_g_cond[[n]] = unique(Reduce(rbind, ct_g_list))
}
ct_g_cond = Reduce(rbind, ct_g_cond)
dim(ct_g_cond)
[1] 23151     6
length(unique(ct_g_cond$interact))
[1] 249
inter_nonss = setdiff(unique(c(inter_df$embolised_simp$id_cp_interaction,
                               inter_df$regenerating_simp$id_cp_interaction)),
                      unique(inter_df$healthy_simp$id_cp_interaction))
inter_nonemb = setdiff(unique(c(inter_df$healthy_simp$id_cp_interaction,
                               inter_df$regenerating_simp$id_cp_interaction)),
                      unique(inter_df$embolised_simp$id_cp_interaction))
inter_nonreg = setdiff(unique(c(inter_df$embolised_simp$id_cp_interaction,
                               inter_df$healthy_simp$id_cp_interaction)),
                      unique(inter_df$regenerating_simp$id_cp_interaction))

cts_g_cond = list()
for(n in names(inter_df)[4:6]){
  df = inter_df[[n]][inter_df[[n]]$cpdb_unique | 
                       inter_df[[n]]$id_cp_interaction %in% c(inter_nonss, inter_nonemb, inter_nonreg),]
  #df = df[df$lr1_nct<=1 | df$lr2_nct<=1,]
  
  cts = unique(c(df$ct1, df$ct2))
  ct_g_list = list()
  for(ct in cts){
    ct_g_list[[ct]] = data.frame("interact" = c(as.character(df$id_cp_interaction[df$ct1==ct]),
                                                as.character(df$id_cp_interaction[df$ct2==ct])),
                                 "gene" = c(as.character(df$gn1[df$ct1==ct]),
                                            as.character(df$gn2[df$ct2==ct])),
                                 "gene_target" = c(as.character(df$gn2[df$ct1==ct]),
                                                   as.character(df$gn1[df$ct2==ct])),
                                 "ct" = ct,
                                 "ct_target" = c(as.character(df$ct2[df$ct1==ct]),
                                                as.character(df$ct1[df$ct2==ct])),
                                 "cond" = n, stringsAsFactors = F)
  }
  cts_g_cond[[n]] = unique(Reduce(rbind, ct_g_list))
}
cts_g_cond = Reduce(rbind, cts_g_cond)
dim(cts_g_cond)
[1] 19383     6
length(unique(cts_g_cond$interact))
[1] 253
ct_g_l = list("cts_g_cond" = cts_g_cond,
              "ct_g_cond" = ct_g_cond)

Get expression for each interaction in each condition

#xxx = merge(unique(rbind(ct_g_cond[,1:3], cts_g_cond[,1:3])), 
#            unique(inter_annot[,c(1,4)]), by = 1, all.x = T)
#write.csv(xxx, file = "results/cell_comm/v3/inter_unique5.csv", row.names = F, col.names = T, quote = F)
inter_annot = read.csv("results/cell_comm/updt/inter_unique5.csv", header = T, stringsAsFactors = F)

inter_annot = unique(inter_annot[,c(1,4)])

description = strsplit(inter_annot$description, ";")
inter_des = lapply(1:length(description), 
                   function(x) rep(inter_annot$interact[x], length(description[[x]])))
inter_annot = data.frame("inter" = unlist(inter_des),
                         "funct" = unlist(description))

inter_annot$funct[inter_annot$funct=="intercellular adhesion"] = "adhesion"
inter_annot$funct[inter_annot$funct=="antibody regulation"] = "immune regulation"
inter_annot$funct[inter_annot$funct=="antigen presentation"] = "immune activity"

write.csv(inter_annot, file = "data/interaction_annotation2.csv", 
          row.names = F, col.names = T, quote = F)
Warning: attempt to set 'col.names' ignored
colnames(inter_annot) = c("interact", "description")

ct_g_cond_ann_l = list()
for(n in names(ct_g_l)){
  ct_g_cond_ann = merge(ct_g_l[[n]], inter_annot, by = 1, all.x = T)
  ct_g_cond_ann = unique(ct_g_cond_ann[,c(1,4,5,6,7)])
  
  ct_g_cond_ann = data.frame("interactions" = rep(as.character(ct_g_cond_ann$interact),2),
                             "celltypes" = c(as.character(ct_g_cond_ann$ct),
                                             as.character(ct_g_cond_ann$ct_target)),
                             "condition" = rep(as.character(ct_g_cond_ann$cond),2),
                             "description" = rep(as.character(ct_g_cond_ann$description),2))
  ct_g_cond_ann$condition = factor(unlist(lapply(strsplit(ct_g_cond_ann$condition, "_"), 
                                                 function(x) x[1])), 
                                   levels = c("healthy", "embolised", "regenerating"))
  
  plt_bar_func = ggplot(ct_g_cond_ann, aes(x = condition, fill = condition))+
    facet_grid(description~celltypes, scales = "free_y")+
    geom_bar()+
    theme_bw()+
    theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1),
          legend.position = "none")
  
  pdf(paste0("results/cell_comm/v3/", n, "_plt_bar_func.pdf"), height = 20, width = 50)
  print(plt_bar_func)
  dev.off()
  
  ct_g_cond_ann_l[[n]] = ct_g_cond_ann
  saveRDS(ct_g_cond_ann, file = paste0("results/cell_comm/v3/", n, "_ann.RDS"))
}

Variability of interactions

exp_list = list()
int_groups = list("cts_g_cond" = unique(c(sig_means_names_l$healthy_simp$id_cp_interaction,
                                    sig_means_names_l$embolised_simp$id_cp_interaction,
                                    sig_means_names_l$regenerating_simp$id_cp_interaction)),
                  "ct_g_cond" = unique(c(sig_means_names_l$healthy$id_cp_interaction,
                                    sig_means_names_l$embolised$id_cp_interaction,
                                    sig_means_names_l$regenerating$id_cp_interaction)))
for(n in names(sig_means_names_l)){
  df = sig_means_names_l[[n]][,c(1,5:6,13:ncol(sig_means_names_l[[n]]))]
  
  #colnames(df) = gsub(".", " ", colnames(df), fixed = T)
  colnames(df) = gsub("gd.T.cells", "gd-T cells", colnames(df), fixed = T)
  colnames(df) = gsub("B.cells", "B cells", colnames(df), fixed = T)
  colnames(df) = gsub("Dividing.endothelial.cells", "Dividing endothelial cells", 
                      colnames(df), fixed = T)
  colnames(df) = gsub("Dividing.cDCs", "Dividing cDCs", colnames(df), fixed = T)
  colnames(df) = gsub("Infiltrating.NK.cells", "Infiltrating NK cells", colnames(df), fixed = T)
  colnames(df) = gsub("Macrophages..HES4..", "Macrophages (HES4+)", colnames(df), fixed = T)
  colnames(df) = gsub("activated.DCs", "activated DCs", colnames(df), fixed = T)
  colnames(df) = gsub("Pericentral.LSEC", "Pericentral LSEC", colnames(df), fixed = T)
  colnames(df) = gsub("Midzonal.LSEC", "Midzonal LSEC", colnames(df), fixed = T)
  colnames(df) = gsub("Periportal.LSEC", "Periportal LSEC", colnames(df), fixed = T)
  colnames(df) = gsub("Periportal.LSEC", "Periportal LSEC", colnames(df), fixed = T)
  colnames(df) = gsub("NK.cells.1", "NK cells 1", colnames(df), fixed = T)
  colnames(df) = gsub("NK.cells.2", "NK cells 2", colnames(df), fixed = T)
  colnames(df) = gsub("NK.cells.3", "NK cells 3", colnames(df), fixed = T)
  colnames(df) = gsub("NK.cells", "NK cells", colnames(df), fixed = T)
  colnames(df) = gsub("CD8.ab.T.cells.1", "CD8 ab-T cells 1", colnames(df), fixed = T)
  colnames(df) = gsub("CD8.ab.T.cells.2", "CD8 ab-T cells 2", colnames(df), fixed = T)
  colnames(df) = gsub("CD8.ab.T.cells.3", "CD8 ab-T cells 3", colnames(df), fixed = T)
  colnames(df) = gsub("CD8.ab.T.cells", "CD8 ab-T cells", colnames(df), fixed = T)
  colnames(df) = gsub("Naive.CD4..T.cells", "Naive CD4+ T cells", colnames(df), fixed = T)
  colnames(df) = gsub("ab.T.cells..stress.", "ab-T cells (stress)", colnames(df), fixed = T)
  colnames(df) = gsub("ab.T.cells..stress.", "ab-T cells (stress)", colnames(df), fixed = T)
  colnames(df) = gsub("Fibroblasts.1", "Fibroblasts 1", colnames(df), fixed = T)
  colnames(df) = gsub("Fibroblasts.2", "Fibroblasts 2", colnames(df), fixed = T)
  colnames(df) = gsub("Fibroblasts.3", "Fibroblasts 3", colnames(df), fixed = T)
  colnames(df) = gsub("Monocytes..secretory.", "Monocytes (secretory)", colnames(df), fixed = T)
  colnames(df) = gsub("Monocytes..TREM2..CD9..", "Monocytes (TREM2+ CD9+)", colnames(df), fixed = T)
  colnames(df) = gsub("Monocytes..IGSF21..GPR34..", "Monocytes (IGSF21+ GPR34+)", 
                      colnames(df), fixed = T)
  colnames(df) = gsub("LSEC..stress.", "LSEC (stress)", colnames(df), fixed = T)
  colnames(df) = gsub("LSEC..remodelling.", "LSEC (remodelling)", colnames(df), fixed = T)
  colnames(df) = gsub("LSEC..interferon.", "LSEC (interferon)", colnames(df), fixed = T)
  colnames(df) = gsub("LSEC..high.MT.2.", "LSEC (high MT 2)", colnames(df), fixed = T)
  colnames(df) = gsub("LSEC..high.MT.1.", "LSEC (high MT 1)", colnames(df), fixed = T)
  colnames(df) = gsub("LSEC..high.MT.", "LSEC (high MT)", colnames(df), fixed = T)
  colnames(df) = gsub("LSEC..fenestr..", "LSEC (fenestr.)", colnames(df), fixed = T)
  colnames(df) = gsub("Kupffer.cells..SUCNR1..", "Kupffer cells (SUCNR1+)", colnames(df), fixed = T)
  colnames(df) = gsub("IgG..Plasma.cells", "IgG+ Plasma cells", colnames(df), fixed = T)
  colnames(df) = gsub("IgA..Plasma.cells", "IgA+ Plasma cells", colnames(df), fixed = T)
  colnames(df) = gsub("EC.non.LSEC", "EC non-LSEC", colnames(df), fixed = T)
  colnames(df) = gsub("Dividing.T.NK.cells", "Dividing T/NK cells", colnames(df), fixed = T)
  colnames(df) = gsub("Kupffer.cells", "Kupffer cells", colnames(df), fixed = T)
  colnames(df) = gsub("TRM.cells", "TRM cells", colnames(df), fixed = T)
  colnames(df) = gsub("MAIT.cells.1", "MAIT cells 1", colnames(df), fixed = T)
  colnames(df) = gsub("MAIT.cells.2", "MAIT cells 2", colnames(df), fixed = T)
  colnames(df) = gsub("MAIT.cells", "MAIT cells", colnames(df), fixed = T)
  colnames(df) = gsub("Lymphatic.EC", "Lymphatic EC", colnames(df), fixed = T)
  colnames(df) = gsub("Stellate.cells", "Stellate cells", colnames(df), fixed = T)
  # head(colnames(df), 60)
  
  df = reshape2::melt(df, id.vars = 1:3)
  df$value[is.na(df$value)] = 0
  df = df %>%
   group_by(id_cp_interaction, gene_a, gene_b, variable) %>%
   summarise(value=(mean(value)), .groups = "keep")
  df = as.data.frame(df)
  
  ns = if(grepl("simp", n)) "cts_g_cond" else "ct_g_cond"
  ct_g_cond_ann = ct_g_cond_ann_l[[ns]]
  sub_df = df[df$id_cp_interaction %in% ct_g_cond_ann$interactions,]
  sub_df = merge(ct_g_l[[ns]], sub_df, all = T, by = 1)
  sub_df = sub_df[sub_df$interact %in% int_groups[[ns]],]
  sub_df$variable = as.character(sub_df$variable)
  sub_df$value[is.na(sub_df$value)] = 0
  
  ct1 = unlist(lapply(strsplit(sub_df$variable, ".", fixed = T), function(x) x[1]))
  ct2 = unlist(lapply(strsplit(sub_df$variable, ".", fixed = T), function(x) x[2]))
  keep = sapply(1:nrow(sub_df), function(x) (sub_df$ct[x]==ct1[x] & sub_df$ct_target[x]==ct2[x]) |
                  (sub_df$ct[x]==ct2[x] & sub_df$ct_target[x]==ct1[x]))
  exp_list[[n]] = sub_df[keep,c(1:6,10)]
}
for(n in names(exp_list)){
  exp_list[[n]] = exp_list[[n]][!is.na(exp_list[[n]]$interact),]
}

ct_int_exp_l = list()
for(simp in c(T, F)){
  n_use = names(exp_list)[grepl("simp", names(exp_list))==simp]
  #ct_int_exp = cbind(exp_list[[n_use[1]]], exp_list[[n_use[3]]]$value, exp_list[[n_use[3]]]$value)
  ct_int_exp = merge(exp_list[[n_use[1]]], exp_list[[n_use[2]]], by = 1:6)
  ct_int_exp = merge(ct_int_exp, exp_list[[n_use[3]]], by = 1:6)
  ct_int_exp$value.x[is.na(ct_int_exp$value.x)] = ct_int_exp$value.y[is.na(ct_int_exp$value.y)] = ct_int_exp$value[is.na(ct_int_exp$value)] = 0
  colnames(ct_int_exp)[7:9] = c("healthy_exp", "embolised_exp", "regenerating_exp")
  ct_int_exp = ct_int_exp[ct_int_exp$healthy_exp>0 | 
                            ct_int_exp$embolised_exp>0 | 
                            ct_int_exp$regenerating_exp>0,]

  tup_list = list()
  keep_row = c()
  for(i in 1:nrow(ct_int_exp)){
    tup1 = paste(c(ct_int_exp$gene[i], ct_int_exp$gene_target[i], ct_int_exp$ct[i], 
                   ct_int_exp$ct_target[i], ct_int_exp$cond[i]), collapse = " ")
    tup2 = paste(c(ct_int_exp$gene_target[i], ct_int_exp$gene[i], ct_int_exp$ct_target[i], 
                   ct_int_exp$ct[i], ct_int_exp$cond[i]), collapse = " ")
    if(!(tup1 %in% tup_list) & !(tup2 %in% tup_list)){
      tup_list = c(tup_list, tup1, tup2)
      keep_row = c(keep_row, T)
    } else{
      keep_row = c(keep_row, F)
    }
  }
  ct_int_exp = ct_int_exp[keep_row,]
  
  ct_int_exp = merge(ct_int_exp, unique(ct_g_cond_ann[,c(1,4)]), by = 1, all = T)
  nn = if(simp) "simp" else "all"
  ct_int_exp_l[[nn]] = ct_int_exp
  ct_int_exp_l[[nn]] = ct_int_exp_l[[nn]][complete.cases(ct_int_exp_l[[nn]]),]
  
  ct_int_exp_l[[nn]]$cond = unlist(lapply(strsplit(ct_int_exp_l[[nn]]$cond, "_"), function(x) x[1]))
}

saveRDS(ct_int_exp_l, file = "results/cell_comm/v3/interact_celltype_exp_group_list.RDS")

GSEA of interactions using mutual information

inter_annot = read.csv("data/interaction_annotation2.csv", header = T)
inter_annot$funct[grepl("imm", inter_annot$funct)] = "immune"
inter_annot$funct[grepl("inflam", inter_annot$funct)] = "immune"
gr = inter_annot$funct
names(gr) = inter_annot$inter
gr_list = tapply(inter_annot$inter, inter_annot$funct, function(x) x)

her_allint_l = list()
for(simp in c(T, F)){
  n_use = names(reform_list)[grepl("simp", names(reform_list))==simp]
  
  he_allint = merge(reform_list[[n_use[1]]][,1:6], reform_list[[n_use[2]]][,1:6], by = 1:3, all = T)
  he_allint$lr1.x[is.na(he_allint$lr1.x)] = he_allint$lr1.y[is.na(he_allint$lr1.x)]
  he_allint$lr2.x[is.na(he_allint$lr2.x)] = he_allint$lr2.y[is.na(he_allint$lr2.x)]
  he_allint = he_allint[,c(1:6,9)]
  her_allint = merge(he_allint, reform_list[[n_use[3]]][,1:6], by = 1:3, all = T)
  her_allint$lr1.x[is.na(her_allint$lr1.x)] = her_allint$lr1[is.na(her_allint$lr1.x)]
  her_allint$lr2.x[is.na(her_allint$lr2.x)] = her_allint$lr2[is.na(her_allint$lr2.x)]
  her_allint = her_allint[,c(1:7,10)]
  her_allint[is.na(her_allint)] = 0
  colnames(her_allint)[4:8] = c("lr1", "lr2", "healthy_exp", "embolised_exp", "regenerating_exp")
  
  # count occurrences per condition. this works bc we're already working with sig means
  her_allint = merge(her_allint, data.frame(table(her_allint$id_cp_interaction[her_allint$healthy_exp>0])), 
                                            by = 1, all.x = T)
  her_allint = merge(her_allint, data.frame(table(her_allint$id_cp_interaction[her_allint$embolised_exp>0])), 
                                            by = 1, all.x = T)
  her_allint = merge(her_allint, 
                     data.frame(table(her_allint$id_cp_interaction[her_allint$regenerating_exp>0])), 
                     by = 1, all.x = T)
  colnames(her_allint)[9:11] = c("healthy_n", "embolised_n", "regenerating_n")
  her_allint[is.na(her_allint)] = 0
  her_allint$ct_pair = factor(paste0(her_allint$ct1, "_", her_allint$ct2))
  
  comb_cond = combn(colnames(her_allint)[6:8],2)
  colnames(comb_cond) = c("he", "hr", "er")
  for(i in colnames(comb_cond)){
    plot_df = her_allint[her_allint[,comb_cond[1,i]]>0 | her_allint[,comb_cond[2,i]]>0,1:12]
    
    exp_df1 = reshape2::dcast(plot_df, formula = id_cp_interaction ~ ct_pair, 
                              value.var = comb_cond[1,i], fill = 0)
    rownames(exp_df1) = exp_df1[,1]
    exp_df1 = exp_df1[,-1]>0
    exp_df2 = reshape2::dcast(plot_df, formula = id_cp_interaction ~ ct_pair, 
                              value.var = comb_cond[2,i], fill = 0)
    rownames(exp_df2) = exp_df2[,1]
    exp_df2 = exp_df2[,-1]>0
    
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) infotheo::mutinformation(exp_df1[x,], exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) e1071::hamming.distance(exp_df1[x,], exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) e1071::hamming.distance(exp_df1[x,],
                                                                        exp_df2[x,])/sum(exp_df1[x,] |
                                                                                           exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) sum(exp_df1[x,] | exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    
    colnames(plot_df)[13:16] = paste0(c("mutInfo_", "hamm_", "hammNorm_", "tot_"), i)
    
    her_allint = merge(her_allint, unique(plot_df[,c(1,13:16)]), all.x = T, by = 1)
  }
  her_allint$mutInfo_er[is.na(her_allint$mutInfo_er)] = 1
  her_allint$mutInfo_hr[is.na(her_allint$mutInfo_hr)] = 1
  her_allint$mutInfo_he[is.na(her_allint$mutInfo_he)] = 1
  her_allint$tot_he[is.na(her_allint$tot_he)] = 0
  her_allint$tot_hr[is.na(her_allint$tot_hr)] = 0
  her_allint$tot_er[is.na(her_allint$tot_er)] = 0
  her_allint$hamm_er[is.na(her_allint$hamm_er)] = 0
  her_allint$hamm_hr[is.na(her_allint$hamm_hr)] = 0
  her_allint$hamm_he[is.na(her_allint$hamm_he)] = 0
  her_allint$hammNorm_er[is.na(her_allint$hammNorm_er)] = 0
  her_allint$hammNorm_he[is.na(her_allint$hammNorm_he)] = 0
  her_allint$hammNorm_hr[is.na(her_allint$hammNorm_hr)] = 0
  
  her_allint$diff_n_he = her_allint$embolised_n-her_allint$healthy_n
  her_allint$diff_n_hr = her_allint$regenerating_n-her_allint$healthy_n
  her_allint$diff_n_er = her_allint$regenerating_n-her_allint$embolised_n
  
  # plot tot vs mutual
  plot_df = unique(her_allint[,c("id_cp_interaction", paste0(c("mutInfo_", "tot_", "diff_n_"), i))])
  plt = ggplot(plot_df, aes(x = tot_er, y = mutInfo_er*(diff_n_er/abs(diff_n_er))))+
    geom_bin2d()+
    scale_x_log10()+
    theme_bw()+
    theme(aspect.ratio = 1)
  print(plt)
  
  nn = if(simp) "simp" else "all"
  her_allint_l[[nn]] = her_allint
}
Aggregation function missing: defaulting to length
Aggregation function missing: defaulting to length
Warning: column names ‘y.x’, ‘y.y’ are duplicated in the resultAggregation function missing: defaulting to length
Aggregation function missing: defaulting to length
Warning: column names ‘y.x’, ‘y.y’ are duplicated in the resultAggregation function missing: defaulting to length
Aggregation function missing: defaulting to length
Warning: column names ‘y.x’, ‘y.y’ are duplicated in the result

saveRDS(her_allint_l, file = "./results/cell_comm/v3/interactions_mutInfo_condComp.RDS")

Save mutual information for LR and cell types

for(n in names(her_allint_l)){
  her_allint = her_allint_l[[n]]
  
  comb_cond = combn(colnames(her_allint)[6:8],2)
  colnames(comb_cond) = c("he", "hr", "er")
  gsea_list = list()
  for(i in colnames(comb_cond)){
    vals = unique(her_allint[,c("id_cp_interaction", paste0("mutInfo_", i))])
    vals2 = vals[,paste0("mutInfo_", i)] 
    names(vals2) = vals$id_cp_interaction
    
    set.seed(1)
    gsea_list[[i]] = liger::bulk.gsea(1-vals2, set.list = gr_list, n.rand = 1000000)
    gsea_list[[i]]$group = rownames(gsea_list[[i]])
    gsea_list[[i]]$comp = i
  }
  
  liger::gsea(1-vals2, geneset = gr_list$immune, plot = T)
  
  # plot GSEA enrichment
  plot_df = Reduce(rbind, gsea_list)
  plot_df$q.val = -log10(plot_df$q.val+0.0000005)*(plot_df$sscore/abs(plot_df$sscore))
  plot_df$comp = factor(plot_df$comp, levels = rev(c("he","hr","er")))
  m = tapply(plot_df$q.val, plot_df$group,mean)
  plot_df$group = factor(plot_df$group, levels = names(m)[order(m, decreasing = F)])
  plt = ggplot(plot_df, aes(x = q.val, y = group, fill = comp))+
    geom_col(position = "dodge")+
    geom_vline(xintercept = c(log10(0.05), -log10(0.05)), linetype = "dashed")+
    geom_vline(xintercept = c(0))+
    labs(x = "q-value x score signal", y = "interaction type", fill = "comparison")+
    theme_bw()+
    theme(axis.text = element_text(colour = "black", size = 7),
          axis.title = element_text(colour = "black", size = 7.5),
          legend.position = c(0,1),
          legend.justification = c(-0.05,1.05),
          legend.title = element_text(size = 7.5),
          legend.text = element_text(size = 7),
          legend.margin = margin(0,0,0,0),
          legend.key.size = unit(0.35, "cm"))
  print(plt)
  
  saveRDS(gsea_list, file = paste0("./results/cell_comm/v3/", n,
                                   "gsea_interactions_mutInfo_condComp.RDS"))
}

Plot interactions

for(n in names(her_allint_l)){
  her_allint = her_allint_l[[n]]
  # select genes from lowest mutual (table - get most common)
  # +
  # mean mutual per cell type (lowest = more change)
  ## plot interactions based on those genes (some are involved in more than one)
  ## heatmap - rows ct; columns genes; gaps between interact; 1 heatmap/cond, same ct ordering
  sub_df1 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","ct1","mutInfo_he")])
  sub_df2 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","ct2","mutInfo_he")])
  ct_mut_he = tapply(c(sub_df1$mutInfo_he, sub_df2$mutInfo_he), 
                     c(sub_df1$ct1, sub_df2$ct2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","ct1","mutInfo_hr")])
  sub_df2 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","ct2","mutInfo_hr")])
  ct_mut_hr = tapply(c(sub_df1$mutInfo_hr, sub_df2$mutInfo_hr), 
                     c(sub_df1$ct1, sub_df2$ct2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","ct1","mutInfo_er")])
  sub_df2 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","ct2","mutInfo_er")])
  ct_mut_er = tapply(c(sub_df1$mutInfo_er, sub_df2$mutInfo_er), 
                     c(sub_df1$ct1, sub_df2$ct2), mean)
  ct_mut_df = cbind(ct_mut_he,ct_mut_hr,ct_mut_er)
  rownames(ct_mut_df) = names(ct_mut_he)
  colnames(ct_mut_df) = c("he", "hr", "er")
  saveRDS(ct_mut_df, file = "./results/cell_comm/v3/ct_select_mutInfo_condComp.RDS")
  
  sub_df1 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","lr1","mutInfo_he")])
  sub_df2 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","lr2","mutInfo_he")])
  lr_mut_he = tapply(c(sub_df1$mutInfo_he, sub_df2$mutInfo_he), 
                     c(sub_df1$lr1, sub_df2$lr2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","lr1","mutInfo_hr")])
  sub_df2 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","lr2","mutInfo_hr")])
  lr_mut_hr = tapply(c(sub_df1$mutInfo_hr, sub_df2$mutInfo_hr), 
                     c(sub_df1$lr1, sub_df2$lr2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","lr1","mutInfo_er")])
  sub_df2 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","lr2","mutInfo_er")])
  lr_mut_er = tapply(c(sub_df1$mutInfo_er, sub_df2$mutInfo_er), 
                     c(sub_df1$lr1, sub_df2$lr2), mean)
  
  lr_cnt_he = table(c(her_allint[her_allint$tot_he>0 & her_allint$mutInfo_he<=0.05,"lr1"],
                      her_allint[her_allint$tot_he>0 & her_allint$mutInfo_he<=0.05,"lr2"]))
  lr_he = merge(lr_cnt_he, lr_mut_he, by.x = 1, by.y = 0)
  lr_cnt_hr = table(c(her_allint[her_allint$tot_hr>0 & her_allint$mutInfo_hr<=0.05,"lr1"],
                      her_allint[her_allint$tot_hr>0 & her_allint$mutInfo_hr<=0.05,"lr2"]))
  lr_hr = merge(lr_cnt_hr, lr_mut_hr, by.x = 1, by.y = 0)
  lr_cnt_er = table(c(her_allint[her_allint$tot_er>0 & her_allint$mutInfo_er<=0.05,"lr1"],
                      her_allint[her_allint$tot_er>0 & her_allint$mutInfo_er<=0.05,"lr2"]))
  lr_er = merge(lr_cnt_er, lr_mut_er, by.x = 1, by.y = 0)
  
  # ECM - which proteins/collagens?; mention TGFB
  # dev - which ligands/receptors
  lr_all = rbind(lr_he, lr_hr, lr_er)
  lr_all$cond = c(rep("he", nrow(lr_he)), rep("hr", nrow(lr_hr)),rep("er", nrow(lr_er)))
  colnames(lr_all) = c("lr", "n_mut.05", "mean_mutInfo", "cond")
  saveRDS(lr_all, file = paste0("./results/cell_comm/v3/", n, "LR_select_mutInfo_condComp.RDS"))
}

Important tables

for(n in names(ct_int_exp_l)){
  ct_int_exp = ct_int_exp_l[[n]]
  r = if(n=="all") 1:3 else 4:6
  
  # interactions
  intdf = unique(Reduce(rbind, reform_list[r])[,c(1,4,5)])
  intdf$intpair = paste0(intdf$lr1, " - ", intdf$lr2)
  
  ct_int_exp_file = unique(ct_int_exp[,c(1,4:5,7:10)])
  ct_int_exp_file$ctpair = paste0(ct_int_exp_file$ct, " - ", ct_int_exp_file$ct_target)
  
  plot_df_int = reshape2::melt(ct_int_exp_file[,c(1,8,4:7)])
  plot_df_int$variable = unlist(lapply(strsplit(as.character(plot_df_int$variable), "_"), 
                                       function(x) x[[1]][1]))
  plot_df_int$variable = factor(plot_df_int$variable, 
                                levels = rev(c("healthy", "embolised", "regenerating")))
  
  sub_plot_df_int = plot_df_int[grepl("Stellate", plot_df_int$ctpair) |
                                  grepl("Kupffer", plot_df_int$ctpair) |
                                  grepl("LSEC", plot_df_int$ctpair),]
  
  sub_plot_df_int = merge(sub_plot_df_int, intdf[,c(1,4)], by = 1, all.x = T)
  
  gg = "ECM"
  plt = ggplot(sub_plot_df_int[sub_plot_df_int$description==gg,], 
         aes(x = ctpair, y = variable, colour = value, size = value))+
    facet_grid(intpair~.)+
    guides(size = guide_legend(title = "exp", reverse = T), 
           colour = guide_legend(title = "exp", reverse = T))+
    geom_point()+
    labs(title = gg)+
    theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1),
          strip.text.y = element_text(angle = 0, size = 8))
  print(plt)
}
Using interact, ctpair, description as id variables

Cell-cell communication networks

Load data to make cell comm networks (NOT USED HERE)

for(n in names(inter_df)){
  write.csv(inter_df[[n]], col.names = T, row.names = F, quote = F,
            file = paste0("results/cell_comm/v3/tables/Interact_", n, "_celltypes_cond.csv"))
}
Warning: attempt to set 'col.names' ignoredWarning: attempt to set 'col.names' ignoredWarning: attempt to set 'col.names' ignoredWarning: attempt to set 'col.names' ignoredWarning: attempt to set 'col.names' ignoredWarning: attempt to set 'col.names' ignored
for(n in names(ct_int_exp_l)){
  ct_int_exp = ct_int_exp_l[[n]]
  write.csv(ct_int_exp, col.names = T, row.names = F, quote = F, 
            file = paste0("results/cell_comm/v3/tables/", n, "interact_celltype_exp_group.csv"))
}
Warning: attempt to set 'col.names' ignoredWarning: attempt to set 'col.names' ignored

Functions used to make cell comm networks

redone_meta = list()
for(cc in unique(allcells_css$Condition)){
  redone_meta[[cc]] = read.table(paste0("results/cell_comm/CellPhoneDB_V3/", 
                                        cc, "/", cc, "_meta_names.txt"), 
                                 sep = "\t", header = T, row.names = 1)
}
redone_meta_all = Reduce(rbind, redone_meta)

allcells_redone = AddMetaData(allcells_css, redone_meta_all)
allcells_redone = allcells_redone[,!is.na(allcells_redone$cell_type)]

Prepare data

makeMedian = function(point_df, edge_df, cl = c("ct2", "maj_g1", "maj_g2")){
  mean_major = data.frame("X1" = tapply(point_df$X1, point_df[,cl[1]], median),
                          "X2" = tapply(point_df$X2, point_df[,cl[1]], median))
  mean_major[,cl[1]] = rownames(mean_major)
  
  edge_df[,cl[2]] = factor(edge_df[,cl[2]], levels = unique(c(edge_df[,cl[2]], edge_df[,cl[3]])))
  edge_df[,cl[3]] = factor(edge_df[,cl[3]], levels = unique(c(as.character(edge_df[,cl[2]]),
                                                              edge_df[,cl[3]])))
  
  maj_mat = table(edge_df[,cl[2]], edge_df[,cl[3]])
  diag(maj_mat) = 0
  edge_major = data.frame(maj_mat + t(maj_mat))
  edge_major = merge(edge_major, mean_major, by.x = 1, by.y = 3)
  edge_major = merge(edge_major, mean_major, by.x = 2, by.y = 3)
  
  clcomb = combn(unique(c(as.character(edge_major$Var1), as.character(edge_major$Var2))), 2)
  keep = c()
  for(j in 1:ncol(clcomb)){
    keep = c(keep, which(edge_major$Var2==clcomb[1,j] & edge_major$Var1==clcomb[2,j]))
  }
  edge_major = edge_major[keep,]
  
  return(list(mean_major = mean_major, edge_major = edge_major))
}

makeMedianCond = function(point_df, edge_df, cl = c("ct", "ct_g1", "ct_g2"), edge_by = "condition"){
  mean_major = data.frame("X1" = tapply(point_df$X1, point_df[,cl[1]], median),
                          "X2" = tapply(point_df$X2, point_df[,cl[1]], median))
  mean_major[,cl[1]] = rownames(mean_major)
  
  edge_df[,cl[2]] = factor(edge_df[,cl[2]], levels = unique(c(edge_df[,cl[2]], edge_df[,cl[3]])))
  edge_df[,cl[3]] = factor(edge_df[,cl[3]], levels = unique(c(as.character(edge_df[,cl[2]]),
                                                              edge_df[,cl[3]])))
  
  edge_l = list()
  for(i in unique(edge_df[,edge_by])){
    sub_edge_df = edge_df[edge_df[,edge_by]==i,]
    maj_mat = table(sub_edge_df[,cl[2]], sub_edge_df[,cl[3]])
    diag(maj_mat) = 0
    edge_major = data.frame(maj_mat + t(maj_mat))
    edge_major = merge(edge_major, mean_major, by.x = 1, by.y = 3)
    edge_major = merge(edge_major, mean_major, by.x = 2, by.y = 3)
    
    # remove repeated
    clcomb = combn(unique(c(as.character(edge_major$Var1), as.character(edge_major$Var2))), 2)
    keep = c()
    for(j in 1:ncol(clcomb)){
      keep = c(keep, which(edge_major$Var2==clcomb[1,j] & edge_major$Var1==clcomb[2,j]))
    }
    
    edge_l[[i]] = edge_major[keep,]
  }
  edge_major = Reduce(rbind, edge_l)
  edge_major[,edge_by] = unlist(lapply(names(edge_l), function(x) rep(x, nrow(edge_l[[x]]))))
  edge_major = edge_major[edge_major$Var2!=edge_major$Var1,]
  
  return(list(mean_major = mean_major, edge_major = edge_major))
}

Plot ligands and receptors with MDS

inter_df = readRDS(file = "results/cell_comm/v3/cond_diff_interact_DE.RDS")

# interactions unique to each condition
unique_inters = c(setdiff(inter_df$healthy$id_cp_interaction, 
                          c(inter_df$embolised$id_cp_interaction, inter_df$regenerating$id_cp_interaction)),
                  setdiff(inter_df$embolised$id_cp_interaction, 
                          c(inter_df$healthy$id_cp_interaction, inter_df$regenerating$id_cp_interaction)),
                  setdiff(inter_df$regenerating$id_cp_interaction, 
                          c(inter_df$embolised$id_cp_interaction, inter_df$healthy$id_cp_interaction)))

# interactions unique to healthy or to emb/regen
comph_inters = c(setdiff(inter_df$healthy$id_cp_interaction, 
                          c(inter_df$embolised$id_cp_interaction, inter_df$regenerating$id_cp_interaction)),
                 setdiff(inter_df$embolised$id_cp_interaction, inter_df$healthy$id_cp_interaction),
                 setdiff(inter_df$regenerating$id_cp_interaction, inter_df$healthy$id_cp_interaction))

# prepare gene pairs per condition
gene_pairs_cond = rbind(unique(inter_df$healthy[,c("id_cp_interaction", "gn1", "gn2")]),
                        unique(inter_df$embolised[,c("id_cp_interaction", "gn1", "gn2")]),
                        unique(inter_df$regenerating[,c("id_cp_interaction", "gn1", "gn2")]))
gene_pairs_cond$condition = c(rep("healthy", nrow(unique(inter_df$healthy[,c("id_cp_interaction", 
                                                                             "gn1", "gn2")]))),
                              rep("embolised", nrow(unique(inter_df$embolised[,c("id_cp_interaction", 
                                                                                 "gn1", "gn2")]))),
                              rep("regenerating", nrow(unique(inter_df$regenerating[,c("id_cp_interaction",
                                                                                       "gn1", "gn2")]))))

# list all LR genes
all_lr_genes = unique(c(as.character(inter_df$healthy$gn1), as.character(inter_df$healthy$gn2),
                        as.character(inter_df$embolised$gn1), as.character(inter_df$embolised$gn2),
                        as.character(inter_df$regenerating$gn1), as.character(inter_df$regenerating$gn2)))
all_lr_genes = all_lr_genes[all_lr_genes %in% rownames(sub_allcells_css@assays$SCT@data)]

# calculate mean per cell type and condition for each LR gene
mean_exp_cond_lr = apply(sub_allcells_css@assays$SCT@data[all_lr_genes,], 1, 
                         function(x) tapply(x, paste0(sub_allcells_css$subpops, 
                                                      "_", sub_allcells_css$Condition), mean))

# determine the cell type and condition with the highest expression
max_cond_ct = rownames(mean_exp_cond_lr)[apply(mean_exp_cond_lr, 2, which.max)]
names(max_cond_ct) = colnames(mean_exp_cond_lr)
max_cond_ct_ct = unlist(lapply(strsplit(max_cond_ct, "_"), function(x) x[1]))
max_cond_ct_cond = unlist(lapply(strsplit(max_cond_ct, "_"), function(x) x[length(x)]))

# correlation of mean expression
cor_cond_lr = cor(mean_exp_cond_lr, method = "sp")

# filter correlation with itself, keep only genes with cor>=0.3
diag(cor_cond_lr) = 0
adj_cond_mat = cor_cond_lr>=0.3

MDS with simplified labels

# build graph, project with MDS
network_cond = graph_from_adjacency_matrix(adj_cond_mat, weighted=T, mode="undirected", diag=F)
l_cond = igraph::layout_with_mds(network_cond)
l_cond = data.frame(l_cond)
l_cond$gene = colnames(adj_cond_mat)
rownames(l_cond) = colnames(adj_cond_mat)

# define all edges, based on CellPhoneDB pairings
tmp_df = merge(gene_pairs_cond, l_cond, by.x = "gn1", by.y = "gene")
edge_cond_df = merge(tmp_df, l_cond, by.x = "gn2", by.y = "gene")
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_ct), by.x = 1, by.y = 0, all.x = T)
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_ct), by.x = 2, by.y = 0, all.x = T)
colnames(edge_cond_df)[9:10] = c("ct_g1", "ct_g2")
# add highest expressing major cell types
edge_cond_df$maj_g1 = ifelse(grepl("LSEC", edge_cond_df$ct_g1), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_df$ct_g1), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_df$ct_g1) |
                                    grepl("Fibroblast", edge_cond_df$ct_g1) |
                                    grepl("VSMC", edge_cond_df$ct_g1), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_df$ct_g1), "Cholangiocytes", 
                                           "Immune"))))
edge_cond_df$maj_g2 = ifelse(grepl("LSEC", edge_cond_df$ct_g2), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_df$ct_g2), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_df$ct_g2) |
                                    grepl("Fibroblast", edge_cond_df$ct_g2) |
                                    grepl("VSMC", edge_cond_df$ct_g2), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_df$ct_g2), "Cholangiocytes", 
                                           "Immune"))))
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_cond), by.x = 1, by.y = 0, all.x = T)
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_cond), by.x = 2, by.y = 0, all.x = T)

# define the vertices of the network
point_cond_df = l_cond
point_cond_df$ct = max_cond_ct_ct[point_cond_df$gene]
point_cond_df$ct2 = ifelse(grepl("LSEC", point_cond_df$ct), "Endothelial",
                      ifelse(grepl("Hepatocytes", point_cond_df$ct), "Hepatocytes",
                             ifelse(grepl("Stellate cells", point_cond_df$ct) |
                                    grepl("VSMC", point_cond_df$ct) |
                                    grepl("Fibroblast", point_cond_df$ct), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", point_cond_df$ct), "Cholangiocytes", 
                                           "Immune"))))
point_cond_df$cond = max_cond_ct_cond[point_cond_df$gene]

# define the median points for each cell type (using max expression)
pe_l = makeMedian(point_cond_df, edge_cond_df, cl = c("ct", "ct_g1", "ct_g2"))

# plot total gene correlation projection and median network
pltboth = ggplot()+
  geom_point(data = point_cond_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.25, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_segment(data = pe_l[[2]], 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq), 
               alpha = 0.15, show.legend = F)+
  geom_point(data = pe_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


pltboth = ggplot()+
  geom_segment(data = edge_cond_df, 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y), 
               alpha = 0.03, show.legend = F)+
  geom_point(data = point_cond_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.6, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_point(data = pe_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


# get median per cell type, per condition - FULL NETWORK
pe_cond_l = makeMedianCond(point_cond_df, edge_cond_df, cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l[[2]][pe_cond_l[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, 
                               size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l[["leg"]] = cowplot::get_legend(plt_cond_l[["healthy"]])

# plot FULL NETWORK median per condition
cowplot::plot_grid(plt_cond_l[[1]]+theme(legend.position = "none"), 
                   plt_cond_l[[2]]+theme(legend.position = "none"),
                   plt_cond_l[[3]]+theme(legend.position = "none"), plt_cond_l$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))


# get median per cell type, per condition - UNIQUE PER CONDTION NETWORK
pe_cond_l_u = makeMedianCond(point_cond_df, edge_cond_df[edge_cond_df$id_cp_interaction %in% unique_inters,],
                             cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l_u = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l_u[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l_u[[2]][pe_cond_l_u[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l_u[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l_u[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l_u[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l_u[["leg"]] = cowplot::get_legend(plt_cond_l_u[["healthy"]])

# plot UNIQUE PER CONDTION NETWORK median per condition
cowplot::plot_grid(plt_cond_l_u[[1]]+theme(legend.position = "none"), 
                   plt_cond_l_u[[2]]+theme(legend.position = "none"),
                   plt_cond_l_u[[3]]+theme(legend.position = "none"), plt_cond_l_u$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))


# get median per cell type, per condition - HEALTHY NETWORK
pe_cond_l_h = makeMedianCond(point_cond_df, edge_cond_df[edge_cond_df$id_cp_interaction %in% inter_df$healthy$id_cp_interaction,], cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l_h = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l_h[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l_h[[2]][pe_cond_l_h[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l_h[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l_h[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l_h[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l_h[["leg"]] = cowplot::get_legend(plt_cond_l_h[["healthy"]])

# plot HEALTHY NETWORK median per condition
cowplot::plot_grid(plt_cond_l_h[[1]]+theme(legend.position = "none"), 
                   plt_cond_l_h[[2]]+theme(legend.position = "none"),
                   plt_cond_l_h[[3]]+theme(legend.position = "none"), plt_cond_l_h$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))


# get median per cell type, per condition - HEALTHY COMPARISON NETWORK
pe_cond_l_ch = makeMedianCond(point_cond_df, 
                             edge_cond_df[edge_cond_df$id_cp_interaction %in% comph_inters,], 
                             cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l_ch = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l_ch[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l_ch[[2]][pe_cond_l_ch[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l_ch[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l_ch[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l_ch[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l_ch[["leg"]] = cowplot::get_legend(plt_cond_l_ch[["healthy"]])

# plot HEALTHY COMPARISON NETWORK median per condition
cowplot::plot_grid(plt_cond_l_ch[[1]]+theme(legend.position = "none"), 
                   plt_cond_l_ch[[2]]+theme(legend.position = "none"),
                   plt_cond_l_ch[[3]]+theme(legend.position = "none"), plt_cond_l_ch$leg, 
                   ncol = 2, rel_widths = c(1,1,1,0.5))

Save network objects

# add labels to points
point_cond_df$ct_mid = point_cond_df$ct
point_cond_df$ct_mid[grepl("NK", point_cond_df$ct)] = "ILC"
point_cond_df$ct_mid[grepl("ILC", point_cond_df$ct)] = "ILC"
point_cond_df$ct_mid[grepl("T cells", point_cond_df$ct)] = "T cells"
point_cond_df$ct_mid[grepl("MAIT", point_cond_df$ct)] = "T cells"
point_cond_df$ct_mid[grepl("Treg", point_cond_df$ct)] = "T cells"
point_cond_df$ct_mid[grepl("Stellate", point_cond_df$ct)] = "Mesenchymal"
point_cond_df$ct_mid[grepl("Fibroblast", point_cond_df$ct)] = "Mesenchymal"
point_cond_df$ct_mid[grepl("VSMC", point_cond_df$ct)] = "Mesenchymal"
point_cond_df$ct_mid[grepl("LSEC", point_cond_df$ct)] = "LSEC"
point_cond_df$ct_mid[grepl("non-LSEC", point_cond_df$ct, 
                                fixed = T)] = "other ECs"
point_cond_df$ct_mid[grepl("Lymphatic EC", point_cond_df$ct, 
                                fixed = T)] = "other ECs"
point_cond_df$ct_mid[grepl("Dividing endothelial cells", point_cond_df$ct, 
                                fixed = T)] = "other ECs"
point_cond_df$ct_mid[grepl("Monocytes", point_cond_df$ct)] = "other Mono-Mac"
point_cond_df$ct_mid[grepl("Macrophages", point_cond_df$ct)] = "other Mono-Mac"
point_cond_df$ct_mid[grepl("cDC", point_cond_df$ct)] = "other Mono-Mac"
point_cond_df$ct_mid[grepl("activated DCs", point_cond_df$ct)] = "other Mono-Mac"
point_cond_df$ct_mid[grepl("Kupffer", point_cond_df$ct)] = "Kupffer cells"
point_cond_df$ct_mid[grepl("Plasma", point_cond_df$ct)] = "B cells"

# add labels to edges
match_df = unique(data.frame("ct" = point_cond_df$ct,
                             "ct_mid" = point_cond_df$ct_mid))
rownames(match_df) = match_df$ct

edge_cond_df$mid_g1 = match_df[edge_cond_df$ct_g1,"ct_mid"]
edge_cond_df$mid_g2 = match_df[edge_cond_df$ct_g2,"ct_mid"]

pe_mid_l = makeMedian(point_cond_df, edge_cond_df, 
                      cl = c("ct_mid", "mid_g1", "mid_g2"))

# plot total gene correlation projection and median network
pltboth = ggplot()+
  geom_point(data = point_cond_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.25, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_segment(data = pe_mid_l[[2]], 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq), 
               alpha = 0.15, show.legend = F)+
  geom_point(data = pe_mid_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct_mid), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


# get median per cell type, per condition - HEALTHY COMPARISON NETWORK
pe_cond_l_mid = makeMedianCond(point_cond_df, 
                                   edge_cond_df[edge_cond_df$id_cp_interaction%in%comph_inters,],
                                   edge_by = "condition", 
                               cl = c("ct_mid", "mid_g1", "mid_g2"))

plt_cond_l_ch = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l_ch[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l_mid[[2]][pe_cond_l_mid[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l_mid[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct_mid), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l_mid[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l_mid[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l_ch[["leg"]] = cowplot::get_legend(plt_cond_l_ch[["healthy"]])

# plot HEALTHY COMPARISON NETWORK median per condition
cowplot::plot_grid(plt_cond_l_ch[[1]]+theme(legend.position = "none"), 
                   plt_cond_l_ch[[2]]+theme(legend.position = "none"),
                   plt_cond_l_ch[[3]]+theme(legend.position = "none"), plt_cond_l_ch$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))

Plot ligands and receptors with UMAP

saveRDS(comph_inters, file = "results/cell_comm/v3/comph_inters.RDS")
save(edge_cond_df, point_cond_df, file = "results/cell_comm/v3/networks_cond.RData")
save(pe_l, pe_cond_l, pe_cond_l_u, pe_cond_l_h, pe_cond_l_ch, 
     file = "results/cell_comm/v3/median_networks_cond.RData")
save(pe_mid_l, pe_cond_l_mid, 
     file = "results/cell_comm/v3/median_networks_cond_midct.RData")

UMAPs with simplified labels

set.seed(2954)
l = uwot::umap(t(mean_exp_cond_lr), metric = "cosine", ret_nn = T, n_epochs = 1000)
l_cond = data.frame(l$embedding)
l_cond$gene = colnames(mean_exp_cond_lr)
rownames(l_cond) = colnames(mean_exp_cond_lr)
tmp_df = merge(gene_pairs_cond, l_cond, by.x = "gn1", by.y = "gene")
edge_cond_umap_df = merge(tmp_df, l_cond, by.x = "gn2", by.y = "gene")
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_ct), by.x = 1, by.y = 0, all.x = T)
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_ct), by.x = 2, by.y = 0, all.x = T)
colnames(edge_cond_umap_df)[9:10] = c("ct_g1", "ct_g2")
edge_cond_umap_df$maj_g1 = ifelse(grepl("LSEC", edge_cond_umap_df$ct_g1), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_umap_df$ct_g1), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_umap_df$ct_g1) |
                                    grepl("VSMC", edge_cond_umap_df$ct_g1) |
                                    grepl("Fibroblast", edge_cond_umap_df$ct_g1), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_umap_df$ct_g1),
                                           "Cholangiocytes", "Immune"))))
edge_cond_umap_df$maj_g2 = ifelse(grepl("LSEC", edge_cond_umap_df$ct_g2), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_umap_df$ct_g2), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_umap_df$ct_g2) |
                                    grepl("VSMC", edge_cond_umap_df$ct_g2) |
                                    grepl("Fibroblast", edge_cond_umap_df$ct_g2), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_umap_df$ct_g2),
                                           "Cholangiocytes", "Immune"))))
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_cond), by.x = 1, by.y = 0, all.x = T)
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_cond), by.x = 2, by.y = 0, all.x = T)
colnames(edge_cond_umap_df)[4] = "cond"

point_cond_umap_df = l_cond
point_cond_umap_df$ct = max_cond_ct_ct[point_cond_umap_df$gene]
point_cond_umap_df$ct2 = ifelse(grepl("LSEC", point_cond_umap_df$ct), "Endothelial",
                      ifelse(grepl("Hepatocytes", point_cond_umap_df$ct), "Hepatocytes",
                             ifelse(grepl("Stellate cells", point_cond_umap_df$ct) |
                                    grepl("VSMC", point_cond_umap_df$ct) |
                                    grepl("Fibroblast", point_cond_umap_df$ct), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", point_cond_umap_df$ct), "Cholangiocytes", 
                                           "Immune"))))
point_cond_umap_df$cond = max_cond_ct_cond[point_cond_umap_df$gene]


pe_umap_l = makeMedian(point_cond_umap_df, edge_cond_umap_df, cl = c("ct", "ct_g1", "ct_g2"))

# plot total gene correlation projection and median network
pltboth = ggplot()+
  geom_point(data = point_cond_umap_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.25, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_segment(data = pe_umap_l[[2]], 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq), 
               alpha = 0.15, show.legend = F)+
  geom_point(data = pe_umap_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


pltboth = ggplot()+
  geom_segment(data = edge_cond_umap_df, 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y), 
               alpha = 0.03, show.legend = F)+
  geom_point(data = point_cond_umap_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.6, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_point(data = pe_umap_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


# get median per cell type, per condition - HEALTHY COMPARISON NETWORK
pe_umap_cond_l_ch = makeMedianCond(point_cond_umap_df, 
                                   edge_cond_umap_df[edge_cond_umap_df$id_cp_interaction%in%comph_inters,],
                                   edge_by = "cond", cl = c("ct", "ct_g1", "ct_g2"))

plt_umap_cond_l_ch = list()
for(cc in unique(pe_cond_l[[2]]$cond)){
  plt_umap_cond_l_ch[[cc]] = ggplot()+
    geom_segment(data = pe_umap_cond_l_ch[[2]][pe_umap_cond_l_ch[[2]]$cond==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_umap_cond_l_ch[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_umap_cond_l_ch[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_umap_cond_l_ch[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_umap_cond_l_ch[["leg"]] = cowplot::get_legend(plt_umap_cond_l_ch[["healthy"]])

# plot HEALTHY COMPARISON NETWORK median per condition
cowplot::plot_grid(plt_umap_cond_l_ch[[1]]+theme(legend.position = "none"), 
                   plt_umap_cond_l_ch[[2]]+theme(legend.position = "none"),
                   plt_umap_cond_l_ch[[3]]+theme(legend.position = "none"), plt_umap_cond_l_ch$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))

Save UMAP network objects

# add labels to points
point_cond_umap_df$ct_mid = point_cond_umap_df$ct
point_cond_umap_df$ct_mid[grepl("NK", point_cond_umap_df$ct)] = "ILC"
point_cond_umap_df$ct_mid[grepl("ILC", point_cond_umap_df$ct)] = "ILC"
point_cond_umap_df$ct_mid[grepl("T cells", point_cond_umap_df$ct)] = "T cells"
point_cond_umap_df$ct_mid[grepl("MAIT", point_cond_umap_df$ct)] = "T cells"
point_cond_umap_df$ct_mid[grepl("Treg", point_cond_umap_df$ct)] = "T cells"
point_cond_umap_df$ct_mid[grepl("Stellate", point_cond_umap_df$ct)] = "Mesenchymal"
point_cond_umap_df$ct_mid[grepl("VSMC", point_cond_umap_df$ct)] = "Mesenchymal"
point_cond_umap_df$ct_mid[grepl("Fibroblast", point_cond_umap_df$ct)] = "Mesenchymal"
point_cond_umap_df$ct_mid[grepl("LSEC", point_cond_umap_df$ct)] = "LSEC"
point_cond_umap_df$ct_mid[grepl("non-LSEC", point_cond_umap_df$ct, 
                                fixed = T)] = "other ECs"
point_cond_umap_df$ct_mid[grepl("Lymphatic EC", point_cond_umap_df$ct, 
                                fixed = T)] = "other ECs"
point_cond_umap_df$ct_mid[grepl("Dividing endothelial cells", point_cond_umap_df$ct, 
                                fixed = T)] = "other ECs"
point_cond_umap_df$ct_mid[grepl("Monocytes", point_cond_umap_df$ct)] = "other Mono-Mac"
point_cond_umap_df$ct_mid[grepl("Macrophages", point_cond_umap_df$ct)] = "other Mono-Mac"
point_cond_umap_df$ct_mid[grepl("cDC", point_cond_umap_df$ct)] = "other Mono-Mac"
point_cond_umap_df$ct_mid[grepl("activated DCs", point_cond_umap_df$ct)] = "other Mono-Mac"
point_cond_umap_df$ct_mid[grepl("Kupffer", point_cond_umap_df$ct)] = "Kupffer cells"
point_cond_umap_df$ct_mid[grepl("Plasma", point_cond_umap_df$ct)] = "B cells"

# add labels to edges
match_df = unique(data.frame("ct" = point_cond_umap_df$ct,
                             "ct_mid" = point_cond_umap_df$ct_mid))
rownames(match_df) = match_df$ct

edge_cond_umap_df$mid_g1 = match_df[edge_cond_umap_df$ct_g1,"ct_mid"]
edge_cond_umap_df$mid_g2 = match_df[edge_cond_umap_df$ct_g2,"ct_mid"]

pe_umap_mid_l = makeMedian(point_cond_umap_df, edge_cond_umap_df, 
                           cl = c("ct_mid", "mid_g1", "mid_g2"))

# plot total gene correlation projection and median network
pltboth = ggplot()+
  geom_point(data = point_cond_umap_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.25, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_segment(data = pe_umap_mid_l[[2]], 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq), 
               alpha = 0.15, show.legend = F)+
  geom_point(data = pe_umap_mid_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct_mid), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


# get median per cell type, per condition - HEALTHY COMPARISON NETWORK
pe_umap_cond_l_mid = makeMedianCond(point_cond_umap_df, 
                                   edge_cond_umap_df[edge_cond_umap_df$id_cp_interaction%in%comph_inters,],
                                   edge_by = "cond", cl = c("ct_mid", "mid_g1", "mid_g2"))

plt_umap_cond_l_ch = list()
for(cc in unique(pe_cond_l[[2]]$cond)){
  plt_umap_cond_l_ch[[cc]] = ggplot()+
    geom_segment(data = pe_umap_cond_l_mid[[2]][pe_umap_cond_l_mid[[2]]$cond==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_umap_cond_l_mid[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct_mid), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_umap_cond_l_mid[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_umap_cond_l_mid[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_umap_cond_l_ch[["leg"]] = cowplot::get_legend(plt_umap_cond_l_ch[["healthy"]])

# plot HEALTHY COMPARISON NETWORK median per condition
cowplot::plot_grid(plt_umap_cond_l_ch[[1]]+theme(legend.position = "none"), 
                   plt_umap_cond_l_ch[[2]]+theme(legend.position = "none"),
                   plt_umap_cond_l_ch[[3]]+theme(legend.position = "none"), plt_umap_cond_l_ch$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))

LS0tCnRpdGxlOiAiQ2VsbCBjb21tdW5pY2F0aW9uIGFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKCiMgR2VuZXJhbCBTZXR1cApTZXR1cCBjaHVuawoKYGBge3IsIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gOCkKa25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBub3JtYWxpemVQYXRoKCIuLiIpKQprbml0cjo6b3B0c19rbml0JGdldCgicm9vdC5kaXIiKQpgYGAKClNldHVwIHJldGljdWxhdGUKCmBgYHtyfQpsaWJyYXJ5KHJldGljdWxhdGUpCmtuaXRyOjprbml0X2VuZ2luZXMkc2V0KHB5dGhvbiA9IHJldGljdWxhdGU6OmVuZ19weXRob24pCnB5X2F2YWlsYWJsZShpbml0aWFsaXplID0gRkFMU0UpCnVzZV9weXRob24oU3lzLndoaWNoKCJweXRob24iKSkKcHlfY29uZmlnKCkKYGBgCgpMb2FkIGxpYnJhcmllcwoKYGBge3J9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KGRhdGEudGFibGUpCmBgYAoKCiMgTG9hZCBhbmQgcHJlcHJvY2VzcyBkYXRhCkxvYWQgZGF0YSAoZnJvbSBhbGwgY2VsbHMpCgpgYGB7cn0KYWxsY2VsbHNfY3NzID0gcmVhZFJEUyhmaWxlID0gImRhdGEvcHJvY2Vzc2VkL2FsbGNlbGxzX2Nzcy5SRFMiKQplbmRfY2VsbHMgPSByZWFkUkRTKGZpbGUgPSAicmVzdWx0cy9lbmRvdGhlbGlhbC9vbmx5X2VuZF9jZWxsc196b24uUkRTIikKaGVwX2NlbGxzID0gcmVhZFJEUyhmaWxlID0gInJlc3VsdHMvem9uYXRpb25fY29uZC9oZXBfY2VsbHNfem9uYXRpb25fcmFuay5SRFMiKQppbW1fY2VsbHMgPSByZWFkUkRTKGZpbGUgPSAicmVzdWx0cy9pbW11bmUvYWxsX2ltbV9jZWxscy5SRFMiKQoKIyBtZXNlbmNoeW1hbCBhbm5vdGF0aW9uCm1lc19hbm5vdCA9IHJlYWQuY3N2KCIuL2RhdGEvcHJvY2Vzc2VkL21lc2VuY2h5bWFsX2ZyZXNoX2Fubm90LmNzdiIsIAogICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLCBoZWFkZXIgPSBULCApCmBgYAoKUHJlcGFyZSBhIGdsb2JhbCBvYmplY3Qgd2l0aCBhbGwgbmVjZXNzYXJ5IG1ldGFkYXRhCgpgYGB7cn0KIyBlbmRvdGhlbGlhbAplbmRwb3BfZGYgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGNvbG5hbWVzKGVuZF9jZWxscyksCiAgICAgICAgICAgICAgICAgICAgICAgInN1YnBvcHMiID0gZW5kX2NlbGxzQG1ldGEuZGF0YSRlbmRvX3NpbXApCiMgaW1tdW5lCmltbXBvcF9kZiA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gY29sbmFtZXMoaW1tX2NlbGxzKSwKICAgICAgICAgICAgICAgICAgICAgICAic3VicG9wcyIgPSBpbW1fY2VsbHNAbWV0YS5kYXRhJGltbXVuZV9hbm5vdCkKIyBoZXBhdG9jeXRlCmhlcHBvcF9kZiA9IGxhcHBseShoZXBfY2VsbHMsIGZ1bmN0aW9uKHgpIGNiaW5kKGNvbG5hbWVzKHgpLCBhcy5jaGFyYWN0ZXIoeCR6b25hdGlvbl9pbnQpKSkKaGVwcG9wX2RmID0gUmVkdWNlKHJiaW5kLCBoZXBwb3BfZGYpCmhlcHBvcF9kZiA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gaGVwcG9wX2RmWywxXSwKICAgICAgICAgICAgICAgICAgICAgICBzdWJwb3BzID0gZmFjdG9yKGhlcHBvcF9kZlssMl0pKQpsZXZlbHMoaGVwcG9wX2RmJHN1YnBvcHMpID0gYygiKC0wLjAwMDk5LDAuMzMzXSIgPSAiSGVwYXRvY3l0ZXNfWjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKDAuMzMzLDAuNjY3XSIgPSAiSGVwYXRvY3l0ZXNfWjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKDAuNjY3LDFdIiA9ICJIZXBhdG9jeXRlc19aMyIpCmhlcHBvcF9kZiRzdWJwb3BzID0gYXMuY2hhcmFjdGVyKGhlcHBvcF9kZiRzdWJwb3BzKQojIG1lc2VuY2h5bWFsCm1lc19kZiA9IG1lc19hbm5vdFssImFubm90YXRpb24iLCBkcm9wID0gRl0KY29sbmFtZXMobWVzX2RmKSA9ICJzdWJwb3BzIgptZXNfZGYkc3VicG9wc1tncmVwbCgiZG91YmxldHMiLG1lc19kZiRzdWJwb3BzKV0gPSAiRG91YmxldHMiCgpzdWJwb3BfZGYgPSByYmluZChlbmRwb3BfZGYsIGltbXBvcF9kZiwgaGVwcG9wX2RmLCBtZXNfZGYpCnN1YnBvcF9kZiRzdWJwb3BzW3N1YnBvcF9kZiRzdWJwb3BzPT0iQ3ljbGluZyBjZWxscyJdID0gIkRpdmlkaW5nIGVuZG90aGVsaWFsIGNlbGxzIgoKIyBhZGQgc3VicG9wIG1ldGFkYXRhCmFsbGNlbGxzX2NzcyA9IEFkZE1ldGFEYXRhKGFsbGNlbGxzX2Nzcywgc3VicG9wX2RmKQphbGxjZWxsc19jc3Mkc3VicG9wc1tpcy5uYShhbGxjZWxsc19jc3Mkc3VicG9wcyldID0gYWxsY2VsbHNfY3NzJGFsbGNlbGxzX21ham9yW2lzLm5hKGFsbGNlbGxzX2NzcyRzdWJwb3BzKV0KYWxsY2VsbHNfY3NzJHN1YnBvcHNbYWxsY2VsbHNfY3NzJHN1YnBvcHM9PSJIZXBhdG9jeXRlLU1vbm9jeXRlIGludGVyYWN0aW9uIl0gPSAiRG91YmxldHMiCgojIHRoZSBjZWxscyBpbiB0aGlzIG9iamVjdCBuYW1lZCAiSGVwYXRvY3l0ZXMiLCAiRW5kb3RoZWxpYWwgY2VsbHMiLCBhbmQgIkRvdWJsZXRzIiBoYXZlIHRvIGJlIHJlbW92ZWQKIyMgdGhlIGZpcnN0IHR3byBhcmUgY2VsbHMgdGhhdCBkaWRuJ3QgcGFzcyB0aGUgaGVwIGFuZCBlbmQgYW5hbHlzaXMKc3ViX2FsbGNlbGxzX2NzcyA9IGFsbGNlbGxzX2Nzc1ssIShhbGxjZWxsc19jc3Mkc3VicG9wcyAlaW4lIGMoIkhlcGF0b2N5dGVzIiwgIkVuZG90aGVsaWFsIGNlbGxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRvdWJsZXRzIikpXQoKIyBhZGQgc2ltcGxpZmllZCBjb2x1bW4gLSBtZXJnZSBzb21lIHBvcHVsYXRpb25zLCBkb24ndCBpbmNsdWRlIGN5Y2xpbmcgcG9wcyBhbmQgc3RyZXNzZWQgVCBjZWxscwpzdWJfYWxsY2VsbHNfY3NzJHN1YnBvcHNfc2ltcCA9IHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wcwpzdWJfYWxsY2VsbHNfY3NzJHN1YnBvcHNfc2ltcFtncmVwbCgiTUFJVCIsIHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wKV0gPSAiTUFJVCBjZWxscyIKc3ViX2FsbGNlbGxzX2NzcyRzdWJwb3BzX3NpbXBbZ3JlcGwoIk5LIGNlbGxzICIsIHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wKV0gPSAiTksgY2VsbHMiCnN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wW2dyZXBsKCJMU0VDIChoaWdoIE1UIiwgc3ViX2FsbGNlbGxzX2NzcyRzdWJwb3BzX3NpbXAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXhlZCA9IFQpXSA9ICJMU0VDIChoaWdoIE1UKSIKc3ViX2FsbGNlbGxzX2NzcyRzdWJwb3BzX3NpbXBbZ3JlcGwoIkNEOCBhYi1UICIsIHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKV0gPSAiQ0Q4IGFiLVQgY2VsbHMiCnNpbXBfYWxsY2VsbHNfY3NzID0gc3ViX2FsbGNlbGxzX2Nzc1ssIShzdWJfYWxsY2VsbHNfY3NzJHN1YnBvcHNfc2ltcCAlaW4lIGMoImFiLVQgY2VsbHMgKHN0cmVzcykiLCAiRGl2aWRpbmcgY0RDcyIsICJEaXZpZGluZyBlbmRvdGhlbGlhbCBjZWxscyIsIkRpdmlkaW5nIFQvTksgY2VsbHMiKSldCmBgYAoKU3Vic2V0IGFuZCBwcm9jZXNzIGVhY2ggY29uZGl0aW9uCgpgYGB7cn0KI2NwZGJfcGF0aCA9ICJyZXN1bHRzL2NlbGxfY29tbS9DZWxsUGhvbmVEQl9WMyIKY3BkYl9wYXRoID0gIi9sb2NhbDEvc2NyYXRjaC90b21hc2dvbWVzL0NlbGxQaG9uZURCX1YzIgoKY29uZF9jZWxscyA9IGxpc3QoKQpmb3IoY29uZCBpbiB1bmlxdWUoc3ViX2FsbGNlbGxzX2Nzc0BtZXRhLmRhdGEkQ29uZGl0aW9uKSl7CiAgaWYoIWRpci5leGlzdHMocGFzdGUwKGNwZGJfcGF0aCwgIi8iLCBjb25kLCAiLyIpKSl7CiAgICBkaXIuY3JlYXRlKHBhc3RlMChjcGRiX3BhdGgsICIvIiwgY29uZCwgIi8iKSkKICB9CiAgIyBzdWJzZXQgY29uZGl0aW9uCiAgY29uZF9jZWxsc1tbY29uZF1dID0gc3ViX2FsbGNlbGxzX2Nzc1ssc3ViX2FsbGNlbGxzX2Nzc0BtZXRhLmRhdGEkQ29uZGl0aW9uPT1jb25kXQogIAogICMgZGVmaW5lIG1ldGFkYXRhCiAgbWV0YSA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXMoY29uZF9jZWxsc1tbY29uZF1dQG1ldGEuZGF0YSksCiAgICAgICAgICAgICAgICAgICAgIkNlbGwiID0gcm93bmFtZXMoY29uZF9jZWxsc1tbY29uZF1dQG1ldGEuZGF0YSksIAogICAgICAgICAgICAgICAgICAgICJjZWxsX3R5cGUiID0gYXMuY2hhcmFjdGVyKGNvbmRfY2VsbHNbW2NvbmRdXUBtZXRhLmRhdGEkc3VicG9wcyksCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgd3JpdGUudGFibGUobWV0YSwgZmlsZSA9IHBhc3RlMChjcGRiX3BhdGgsICIvIiwgY29uZCwgIi8iLCBjb25kLCAiX21ldGFfbmFtZXMudHh0IiksIAogICAgICAgICAgICBzZXAgPSAiXHQiLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCiAgCiAgIyBub3JtYWxpc2UgYW5kIHNhdmUKICBjb25kX2NlbGxzW1tjb25kXV0gPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKGNvbmRfY2VsbHNbW2NvbmRdXSwgZG8uY29ycmVjdC51bWkgPSBULCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcz1jKCJ1bmlxdWVfbmFtZSIsICJuQ291bnRfUk5BIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5ydi50aCA9IDEsIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybi5vbmx5LnZhci5nZW5lcyA9IEYsIHZlcmJvc2UgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMubiA9IE5VTEwpKQogIAogIGRhdCA9IGNiaW5kKHJvd25hbWVzKGNvbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGEpLAogICAgICAgICAgICAgIE1hdHJpeDo6YXMubWF0cml4KGNvbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGEpKQogIGNvbG5hbWVzKGRhdClbMV0gPSAiR2VuZSIKICB3cml0ZS50YWJsZShkYXQsIGZpbGUgPSBwYXN0ZTAoY3BkYl9wYXRoLCAiLyIsIGNvbmQsICIvIiwgY29uZCwgIl9leHBfbm9ybS50eHQiKSwgCiAgICAgICAgICAgICAgc2VwID0gIlx0IiwgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRiwgcXVvdGUgPSBGKQogIAogIAogIGlmKCFkaXIuZXhpc3RzKHBhc3RlMChjcGRiX3BhdGgsICIvIiwgY29uZCwgIl9zaW1wLyIpKSl7CiAgICBkaXIuY3JlYXRlKHBhc3RlMChjcGRiX3BhdGgsICIvIiwgY29uZCwgIl9zaW1wLyIpKQogIH0KICAjIHN1YnNldCBjb25kaXRpb24KICBjb25kX2NlbGxzW1tjb25kXV0gPSBzaW1wX2FsbGNlbGxzX2Nzc1ssc2ltcF9hbGxjZWxsc19jc3NAbWV0YS5kYXRhJENvbmRpdGlvbj09Y29uZF0KICAKICAjIGRlZmluZSBtZXRhZGF0YQogIG1ldGFfc2ltcCA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXMoY29uZF9jZWxsc1tbY29uZF1dQG1ldGEuZGF0YSksCiAgICAgICAgICAgICAgICAgICAgICAgICAiQ2VsbCIgPSByb3duYW1lcyhjb25kX2NlbGxzW1tjb25kXV1AbWV0YS5kYXRhKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiY2VsbF90eXBlIiA9IGFzLmNoYXJhY3Rlcihjb25kX2NlbGxzW1tjb25kXV1AbWV0YS5kYXRhJHN1YnBvcHNfc2ltcCksCiAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICB3cml0ZS50YWJsZShtZXRhX3NpbXAsIGZpbGUgPSBwYXN0ZTAoY3BkYl9wYXRoLCAiLyIsIGNvbmQsICJfc2ltcC8iLCBjb25kLCAiX21ldGFfbmFtZXMudHh0IiksIAogICAgICAgICAgICBzZXAgPSAiXHQiLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCiAgIAogICMgbm9ybWFsaXNlIGFuZCBzYXZlCiAgY29uZF9jZWxsc1tbY29uZF1dID0gc3VwcHJlc3NXYXJuaW5ncyhTQ1RyYW5zZm9ybShjb25kX2NlbGxzW1tjb25kXV0sIGRvLmNvcnJlY3QudW1pID0gVCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJzLnRvLnJlZ3Jlc3M9YygidW5pcXVlX25hbWUiLCAibkNvdW50X1JOQSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMucnYudGggPSAxLCBzZWVkLnVzZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ub25seS52YXIuZ2VuZXMgPSBGLCB2ZXJib3NlID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLm4gPSBOVUxMKSkKICAKICBkYXQgPSBjYmluZChyb3duYW1lcyhjb25kX2NlbGxzW1tjb25kXV1AYXNzYXlzJFNDVEBkYXRhKSwKICAgICAgICAgICAgICBNYXRyaXg6OmFzLm1hdHJpeChjb25kX2NlbGxzW1tjb25kXV1AYXNzYXlzJFNDVEBkYXRhKSkKICBjb2xuYW1lcyhkYXQpWzFdID0gIkdlbmUiCiAgd3JpdGUudGFibGUoZGF0LCBmaWxlID0gcGFzdGUwKGNwZGJfcGF0aCwgIi8iLCBjb25kLCAiX3NpbXAvIiwgY29uZCwgIl9leHBfbm9ybS50eHQiKSwgCiAgICAgICAgICAgICAgc2VwID0gIlx0IiwgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRiwgcXVvdGUgPSBGKQp9CmBgYAoKCiMjIFJ1bm5pbmcgQ2VsbFBob25lREIKQ29tbWFuZHMgZm9yIHJ1bm5pbiBDZWxsUGhvbmVEQiB3aWxsIGJlIHdyaXR0ZW4gaGVyZS4gUmVzdWx0cyBhcmUgb3V0cHV0IHRvIHRoZSBgbG9jYWwxYCBkaXNrIHRvIGF2b2lkIEkvTyBpc3N1ZXMsIGJ1dCB0aGUgb3V0cHV0IGZvbGRlcnMgc2hvdWxkIHRoZW4gYmUgY29waWVkIHRvOiBgcmVzdWx0cy9jZWxsX2NvbW1faGVhbHRoeS9DZWxsUGhvbmVEQl9ydW5zYC4gIApOT1RFOiBmb3Igc29tZSByZWFzb24gSSdtIGdldHRpbmcgYSBTZWdGYXVsdCB3aGVuIHRyeWluZyB0byBydW4gdGhlIGhlYXRtYXBfcGxvdCBjb21tYW5kLiBUaGlzIHdhcyBydW4gb24gdGhlIEV1bGVyIHNlcnZlciBpbnN0ZWFkLgoKYGBge3J9CmZvcihuIGluIG5hbWVzKGNvbmRfY2VsbHMpKXsKICBjb21tMSA9IHBhc3RlMCgiY2VsbHBob25lZGIgbWV0aG9kIHN0YXRpc3RpY2FsX2FuYWx5c2lzIC9sb2NhbDEvc2NyYXRjaC90b21hc2dvbWVzL0NlbGxQaG9uZURCX1YzLyIsIG4sICIvIiwgbiwgIl9tZXRhX25hbWVzLnR4dCAvbG9jYWwxL3NjcmF0Y2gvdG9tYXNnb21lcy9DZWxsUGhvbmVEQl9WMy8iLCBuLCAiLyIsIG4sICJfZXhwX25vcm0udHh0IC0tdGhyZWFkcz0xMiAtLW91dHB1dC1wYXRoPS9sb2NhbDEvc2NyYXRjaC90b21hc2dvbWVzL2xpdmVyL0NlbGxQaG9uZURCX1YzLyIsIG4sICIgLS1wcm9qZWN0LW5hbWUgIiwgbiwgIiAtLWNvdW50cy1kYXRhIGdlbmVfbmFtZSIpCiAgY29tbTIgPSBwYXN0ZTAoImNwIC1yIC9sb2NhbDEvc2NyYXRjaC90b21hc2dvbWVzL2xpdmVyL0NlbGxQaG9uZURCX1YzLyIsIG4sICIvIiwgbiwgIiByZXN1bHRzL2NlbGxfY29tbS9DZWxsUGhvbmVEQl9WMy8iKQogIGNvbW0zID0gcGFzdGUwKCJjcCAtciAvbG9jYWwxL3NjcmF0Y2gvdG9tYXNnb21lcy9DZWxsUGhvbmVEQl9WMy8iLCBuLCAiLyIsIG4sICJfbWV0YV9uYW1lcy50eHQgcmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfVjMvIiwgbiwgIi8iKQogIGNvbW00ID0gcGFzdGUwKCJjZWxscGhvbmVkYiBwbG90IGhlYXRtYXBfcGxvdCAtLXB2YWx1ZXMtcGF0aCAuL3Jlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX1YzLyIsIG4sICIvcHZhbHVlcy50eHQgLS1vdXRwdXQtcGF0aCAuL3Jlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX1YzLyIsIG4sICIvIC0tY291bnQtbmFtZSBjb3VudHNfaGVhdC5wZGYgLS1jb3VudC1uZXR3b3JrLW5hbWUgY291bnRfbmV0LnR4dCAtLWludGVyYWN0aW9uLWNvdW50LW5hbWUgY291bnRfaW50ZXIudHh0IHJlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX1YzLyIsIG4sICIvIiwgbiwgIl9tZXRhX25hbWVzLnR4dCIpCiAgCiAgcHJpbnQobikKICBwcmludChjb21tMSkKICBwcmludChjb21tMikKICBwcmludChjb21tMykKICBwcmludChjb21tNCkKICBwcmludCgiLiIpCn0KYGBgCgpgYGB7cn0KZm9yKG4gaW4gbmFtZXMoY29uZF9jZWxscykpewogIGNvbW0xID0gcGFzdGUwKCJjZWxscGhvbmVkYiBtZXRob2Qgc3RhdGlzdGljYWxfYW5hbHlzaXMgL2xvY2FsMS9zY3JhdGNoL3RvbWFzZ29tZXMvQ2VsbFBob25lREJfVjMvIiwgbiwgIl9zaW1wLyIsIG4sICJfbWV0YV9uYW1lcy50eHQgL2xvY2FsMS9zY3JhdGNoL3RvbWFzZ29tZXMvQ2VsbFBob25lREJfVjMvIiwgbiwgIl9zaW1wLyIsIG4sICJfZXhwX25vcm0udHh0IC0tdGhyZWFkcz0xMiAtLW91dHB1dC1wYXRoPS9sb2NhbDEvc2NyYXRjaC90b21hc2dvbWVzL2xpdmVyL0NlbGxQaG9uZURCX1YzLyIsIG4sICJfc2ltcCAtLXByb2plY3QtbmFtZSAiLCBuLCAiX3NpbXAgLS1jb3VudHMtZGF0YSBnZW5lX25hbWUiKQogIGNvbW0yID0gcGFzdGUwKCJjcCAtciAvbG9jYWwxL3NjcmF0Y2gvdG9tYXNnb21lcy9saXZlci9DZWxsUGhvbmVEQl9WMy8iLCBuLCAiX3NpbXAvIiwgbiwgIl9zaW1wIHJlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX1YzLyIpCiAgY29tbTMgPSBwYXN0ZTAoImNwIC1yIC9sb2NhbDEvc2NyYXRjaC90b21hc2dvbWVzL0NlbGxQaG9uZURCX1YzLyIsIG4sICJfc2ltcC8iLCBuLCAiX21ldGFfbmFtZXMudHh0IHJlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX1YzLyIsIG4sICJfc2ltcC8iKQogIGNvbW00ID0gcGFzdGUwKCJjZWxscGhvbmVkYiBwbG90IGhlYXRtYXBfcGxvdCAtLXB2YWx1ZXMtcGF0aCAuL3Jlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX1YzLyIsIG4sICJfc2ltcC9wdmFsdWVzLnR4dCAtLW91dHB1dC1wYXRoIC4vcmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfVjMvIiwgbiwgIl9zaW1wLyAtLWNvdW50LW5hbWUgY291bnRzX2hlYXQucGRmIC0tY291bnQtbmV0d29yay1uYW1lIGNvdW50X25ldC50eHQgLS1pbnRlcmFjdGlvbi1jb3VudC1uYW1lIGNvdW50X2ludGVyLnR4dCAvbG9jYWwxL3NjcmF0Y2gvdG9tYXNnb21lcy9DZWxsUGhvbmVEQl9WMy8iLCBuLCAiX3NpbXAvIiwgbiwgIl9tZXRhX25hbWVzLnR4dCIpCiAgCiAgcHJpbnQobikKICBwcmludChjb21tMSkKICBwcmludChjb21tMikKICBwcmludChjb21tMykKICBwcmludChjb21tNCkKICBwcmludCgiLiIpCn0KYGBgCgoKCiMgUHJvY2VzcyByZXN1bHRzCkxvYWQgcmVzdWx0cwoKYGBge3J9CmNwZGJfcGF0aCA9ICJyZXN1bHRzL2NlbGxfY29tbS9DZWxsUGhvbmVEQl9WMyIKCnNpZ19tZWFuc19uYW1lc19sID0gbGlzdCgpCmRlY19sID0gbGlzdCgpCm5ldF9uYW1lc19sID0gbGlzdCgpCm1ldGFfbCA9IGxpc3QoKQpjb25kcyA9IHVuaXF1ZShhbGxjZWxsc19jc3NAbWV0YS5kYXRhJENvbmRpdGlvbikKZm9yKG4gaW4gYyhjb25kcywgcGFzdGUwKGNvbmRzLCAiX3NpbXAiKSkpewogIG5zID0gc3Ryc3BsaXQobiwgIl8iKVtbMV1dWzFdCiAgc2lnX21lYW5zX25hbWVzX2xbW25dXSA9IHJlYWQudGFibGUocGFzdGUwKGNwZGJfcGF0aCwgIi8iLCBuLCAiL3NpZ25pZmljYW50X21lYW5zLnR4dCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsIHNlcCA9ICJcdCIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIGRlY19sW1tuXV0gPSByZWFkLnRhYmxlKHBhc3RlMChjcGRiX3BhdGgsICIvIiwgbiwgIi9kZWNvbnZvbHV0ZWQudHh0IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgc2VwID0gIlx0IikKICBjb2xuYW1lcyhkZWNfbFtbbl1dKSA9IGdzdWIoIi4iLCAiICIsIGNvbG5hbWVzKGRlY19sW1tuXV0pLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJnZCBUIGNlbGxzIl0gPSAiZ2QtVCBjZWxscyIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09Ik1hY3JvcGhhZ2VzICBIRVM0ICAiXSA9ICJNYWNyb3BoYWdlcyAoSEVTNCspIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iQ0Q4IGFiIFQgY2VsbHMiXSA9ICJDRDggYWItVCBjZWxscyIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkNEOCBhYiBUIGNlbGxzIDEiXSA9ICJDRDggYWItVCBjZWxscyAxIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iQ0Q4IGFiIFQgY2VsbHMgMiJdID0gIkNEOCBhYi1UIGNlbGxzIDIiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJDRDggYWIgVCBjZWxscyAzIl0gPSAiQ0Q4IGFiLVQgY2VsbHMgMyIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09Ik5haXZlIENENCAgVCBjZWxscyJdID0gIk5haXZlIENENCsgVCBjZWxscyIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09ImFiIFQgY2VsbHMgIHN0cmVzcyAiXSA9ICJhYi1UIGNlbGxzIChzdHJlc3MpIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTW9ub2N5dGVzICBzZWNyZXRvcnkgIl0gPSAiTW9ub2N5dGVzIChzZWNyZXRvcnkpIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTW9ub2N5dGVzICBUUkVNMiAgQ0Q5ICAiXSA9ICJNb25vY3l0ZXMgKFRSRU0yKyBDRDkrKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09Ik1vbm9jeXRlcyAgSUdTRjIxICBHUFIzNCAgIl0gPSAiTW9ub2N5dGVzIChJR1NGMjErIEdQUjM0KykiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJMU0VDICBzdHJlc3MgIl0gPSAiTFNFQyAoc3RyZXNzKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkxTRUMgIHJlbW9kZWxsaW5nICJdID0gIkxTRUMgKHJlbW9kZWxsaW5nKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkxTRUMgIGludGVyZmVyb24gIl0gPSAiTFNFQyAoaW50ZXJmZXJvbikiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJMU0VDICBoaWdoIE1UIDIgIl0gPSAiTFNFQyAoaGlnaCBNVCAyKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkxTRUMgIGhpZ2ggTVQgMSAiXSA9ICJMU0VDIChoaWdoIE1UIDEpIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTFNFQyAgaGlnaCBNVCAiXSA9ICJMU0VDIChoaWdoIE1UKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkxTRUMgIGZlbmVzdHIgICJdID0gIkxTRUMgKGZlbmVzdHIuKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09Ikt1cGZmZXIgY2VsbHMgIFNVQ05SMSAgIl0gPSAiS3VwZmZlciBjZWxscyAoU1VDTlIxKykiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJJZ0cgIFBsYXNtYSBjZWxscyJdID0gIklnRysgUGxhc21hIGNlbGxzIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iSWdBICBQbGFzbWEgY2VsbHMiXSA9ICJJZ0ErIFBsYXNtYSBjZWxscyIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkNEOCBhYiBUIGNlbGxzICBzdHJlc3MgIl0gPSAiQ0Q4IGFiLVQgY2VsbHMgKHN0cmVzcykiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJFQyBub24gTFNFQyJdID0gIkVDIG5vbi1MU0VDIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iRGl2aWRpbmcgVCBOSyBjZWxscyJdID0gIkRpdmlkaW5nIFQvTksgY2VsbHMiCiAgbmV0X25hbWVzX2xbW25dXSA9IHJlYWQudGFibGUocGFzdGUwKGNwZGJfcGF0aCwgIi8iLCBuLCAiL2NvdW50X25ldC50eHQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULCBzZXAgPSAiXHQiLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKICBtZXRhX2xbW25dXSA9IGlmKCFncmVwbCgic2ltcCIsIG4pKXsKICAgIHJlYWQudGFibGUocGFzdGUwKGNwZGJfcGF0aCwgIi8iLCBuLCAiLyIsIG4sICJfbWV0YV9uYW1lcy50eHQiKSxoZWFkZXIgPSBULHNlcCA9ICJcdCIpCiAgfSBlbHNlewogICAgcmVhZC50YWJsZShwYXN0ZTAoY3BkYl9wYXRoLCAiLyIsIG4sICIvIiwgbnMsICJfbWV0YV9uYW1lcy50eHQiKSxoZWFkZXIgPSBULHNlcCA9ICJcdCIpCiAgfQp9CnNhdmVSRFMoZGVjX2wsIGZpbGUgPSAicmVzdWx0cy9jZWxsX2NvbW0vdjMvZGVjb252b2x1dGVkX2xpc3QuUkRTIikKc2F2ZVJEUyhuZXRfbmFtZXNfbCwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS92My9jb3VudF9uZXRfbGlzdC5SRFMiKQpgYGAKClJlZm9ybWF0IHNpZ25pZmljYW50IG1lYW5zIG1hdHJpeAoKYGBge3J9CnJlZm9ybV9saXN0ID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKG1ldGFfbCkpewogIHNpbXBfcmVmb3JtID0gcmVzaGFwZTI6Om1lbHQoc2lnX21lYW5zX25hbWVzX2xbW25dXVssYygxOjQsNzo5LDEzOm5jb2woc2lnX21lYW5zX25hbWVzX2xbW25dXSkpXSkKICBzaW1wX3JlZm9ybSA9IHNpbXBfcmVmb3JtW2NvbXBsZXRlLmNhc2VzKHNpbXBfcmVmb3JtKSxdCiAgCiAgbHIxID0gYygpCiAgbHIyID0gYygpCiAgZm9yKGkgaW4gMTpucm93KHNpbXBfcmVmb3JtKSl7CiAgICBpZihncmVwbCgiY29tcGxleCIsIHNpbXBfcmVmb3JtJHBhcnRuZXJfYVtpXSkpewogICAgICBscjEgPSBjKGxyMSwgc3Vic3RyKHNpbXBfcmVmb3JtJHBhcnRuZXJfYVtpXSwgOSwxMDApKQogICAgfSBlbHNlewogICAgICBscjEgPSBjKGxyMSwgc3Ryc3BsaXQoc2ltcF9yZWZvcm0kaW50ZXJhY3RpbmdfcGFpcltpXSwgIl8iKVtbMV1dWzFdKQogICAgfQogICAgaWYoZ3JlcGwoImNvbXBsZXgiLCBzaW1wX3JlZm9ybSRwYXJ0bmVyX2JbaV0pKXsKICAgICAgbHIyID0gYyhscjIsIHN1YnN0cihzaW1wX3JlZm9ybSRwYXJ0bmVyX2JbaV0sIDksMTAwKSkKICAgIH0gZWxzZXsKICAgICAgc3BsdGxlbiA9IGxlbmd0aChzdHJzcGxpdChzaW1wX3JlZm9ybSRpbnRlcmFjdGluZ19wYWlyW2ldLCAiXyIpW1sxXV0pCiAgICAgIGxyMiA9IGMobHIyLCBzdHJzcGxpdChzaW1wX3JlZm9ybSRpbnRlcmFjdGluZ19wYWlyW2ldLCAiXyIpW1sxXV1bc3BsdGxlbl0pCiAgICB9CiAgfQogIHNpbXBfcmVmb3JtJGxyMSA9IGxyMQogIHNpbXBfcmVmb3JtJGxyMiA9IGxyMgogIAogIHNpbXBfcmVmb3JtJGN0MSA9IE5BCiAgc2ltcF9yZWZvcm0kY3QyID0gTkEKICBkb25lc3QgPSByZXAoTkEsIGxlbmd0aChzaW1wX3JlZm9ybSRjdDEpKQogIGRvbmVlbiA9IHJlcChOQSwgbGVuZ3RoKHNpbXBfcmVmb3JtJGN0MikpCiAgZm9yKGN0IGluIHNvcnQodW5pcXVlKG1ldGFfbFtbbl1dJGNlbGxfdHlwZSksIGRlY3JlYXNpbmcgPSBUKSl7CiAgICBjdF9tb2QgPSBnc3ViKCItIiwgIiAiLCBjdCwgZml4ZWQgPSBUKQogICAgY3RfbW9kID0gZ3N1YigiKyIsICIgIiwgY3RfbW9kLCBmaXhlZCA9IFQpCiAgICBjdF9tb2QgPSBnc3ViKCIvIiwgIiAiLCBjdF9tb2QsIGZpeGVkID0gVCkKICAgIGN0X21vZCA9IGdzdWIoIigiLCAiICIsIGN0X21vZCwgZml4ZWQgPSBUKQogICAgY3RfbW9kID0gZ3N1YigiKSIsICIgIiwgY3RfbW9kLCBmaXhlZCA9IFQpCiAgICBjdF9tb2QgPSBnc3ViKCIuIiwgIiAiLCBjdF9tb2QsIGZpeGVkID0gVCkKICAgIHN0ID0gZ3JlcGwocGFzdGUwKCJeIixjdF9tb2QsICIgIiksIGdzdWIoIi4iLCAiICIsIHNpbXBfcmVmb3JtJHZhcmlhYmxlLCBmaXhlZCA9IFQpLCBmaXhlZCA9IEYpCiAgICBlbiA9IGdyZXBsKHBhc3RlMCgiICIsY3RfbW9kLCIkIiksIGdzdWIoIi4iLCAiICIsIHNpbXBfcmVmb3JtJHZhcmlhYmxlLCBmaXhlZCA9IFQpLCBmaXhlZCA9IEYpCiAgICBzaW1wX3JlZm9ybSRjdDFbc3QgJiBpcy5uYShkb25lc3QpXSA9IGN0CiAgICBzaW1wX3JlZm9ybSRjdDJbZW4gJiBpcy5uYShkb25lZW4pXSA9IGN0CiAgICBkb25lc3Rbc3RdID0gVAogICAgZG9uZWVuW2VuXSA9IFQKICB9CiAgI2NvbG5hbWVzKGRlY19sW1tuXV0pWyEoY29sbmFtZXMoZGVjX2xbW25dXSkgJWluJSB1bmlxdWUoc2ltcF9yZWZvcm0kY3QyKSldCiAgCiAgIyBkaXJlY3Rpb246IHJlY2VwdG9ycyBhY3RpdmF0ZSBpbnRlcm5hbCBzaWduYWwgLSB0aGF0IGlzIHRoZSBkaXJlY3Rpb24gb2YgdGhlIHNpZ25hbGxpbmcKICAjIyBpZiBib3RoIGFyZSB0cnVlLCB0aGUgIm5ldCBzaWduYWwiIGlzIDAKICBzaW1wX3JlZm9ybSRkaXIgPSBpZmVsc2Uoc2ltcF9yZWZvcm0kcmVjZXB0b3JfYT09c2ltcF9yZWZvcm0kcmVjZXB0b3JfYiwgMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzaW1wX3JlZm9ybSRyZWNlcHRvcl9hPT0iVHJ1ZSIgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2ltcF9yZWZvcm0kcmVjZXB0b3JfYj09IkZhbHNlIiwgLTEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc2ltcF9yZWZvcm0kcmVjZXB0b3JfYT09IkZhbHNlIiAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaW1wX3JlZm9ybSRyZWNlcHRvcl9iPT0iVHJ1ZSIsIDEsIE5BKSkpCiAgCiAgc2ltcF9yZWZvcm0gPSBzaW1wX3JlZm9ybVssYygiaWRfY3BfaW50ZXJhY3Rpb24iLCAiY3QxIiwgImN0MiIsICJscjEiLCAibHIyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiLCAiZGlyIildCiAgCiAgZm9yKGkgaW4gMTpucm93KHNpbXBfcmVmb3JtKSl7CiAgICBpZihzaW1wX3JlZm9ybVtpLCJkaXIiXT09LTEpewogICAgICB0bXAgPSBzaW1wX3JlZm9ybVtpLCJjdDIiXQogICAgICBzaW1wX3JlZm9ybVtpLCJjdDIiXSA9IHNpbXBfcmVmb3JtW2ksImN0MSJdCiAgICAgIHNpbXBfcmVmb3JtW2ksImN0MSJdID0gdG1wCiAgICAgIAogICAgICB0bXAgPSBzaW1wX3JlZm9ybVtpLCJscjIiXQogICAgICBzaW1wX3JlZm9ybVtpLCJscjIiXSA9IHNpbXBfcmVmb3JtW2ksImxyMSJdCiAgICAgIHNpbXBfcmVmb3JtW2ksImxyMSJdID0gdG1wCiAgICAgIAogICAgICBzaW1wX3JlZm9ybVtpLCJkaXIiXT0xCiAgICB9CiAgfQogIAogcmVmb3JtX2xpc3RbW25dXSA9IHNpbXBfcmVmb3JtCn0Kc2F2ZVJEUyhyZWZvcm1fbGlzdCwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS92My9yZWZvcm1fbGlzdC5SRFMiKQpgYGAKClBsb3QgaW50ZXJhY3Rpb24gY291bnRzCgpgYGB7cn0KaW50ZXJfY291bnRzX2wgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMobmV0X25hbWVzX2wpKXsKICBpbnRlcl9jb3VudHMgPSByZXNoYXBlMjo6ZGNhc3QoZGF0YSA9IG5ldF9uYW1lc19sW1tuXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtdWxhID0gU09VUkNFflRBUkdFVCwgdmFsdWUudmFyID0gImNvdW50IikKICByb3duYW1lcyhpbnRlcl9jb3VudHMpID0gaW50ZXJfY291bnRzWywxXQogIGludGVyX2NvdW50cyA9IGludGVyX2NvdW50c1ssLTFdCiAgCiAgcGRmKHBhc3RlMCgicmVzdWx0cy9jZWxsX2NvbW0vdjMvIiwgbiwgIl9udW1iZXJfb2ZfaW50ZXJhY3Rpb25zLnBkZiIpLCB1c2VEaW5nYmF0cyA9IEYsIAogICAgICBoZWlnaHQgPSAxMCwgd2lkdGggPSAxMCkKICBwaGVhdG1hcDo6cGhlYXRtYXAoaW50ZXJfY291bnRzLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIiwgbWFpbiA9ICJBbGwgY2x1c3RlcnMiKQogIGRldi5vZmYoKQogIGludGVyX2NvdW50c19sW1tuXV0gPSBpbnRlcl9jb3VudHMKfQpgYGAKCk1ha2luZyB0d28gY2VsbCB0eXBlIGJ5IGludGVyYWN0aW9uIG1hdHJpY2VzOiBvbmUgaGFzIHRoZSBsaWdhbmQgbWVhbiwgYW5vdGhlciB0aGUgcmVjZXB0b3IgbWVhbgoKYGBge3J9CnNjb3JpbmdfbWF0cyA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhyZWZvcm1fbGlzdCkpewogIGNsX3JlZm9ybSA9IHJlZm9ybV9saXN0W1tuXV0KICAjIGFkZCByZXZlcnNlZCBub24tZGlyZWN0ZWQgaW50ZXJhY3Rpb25zLCB0byBjb25zaWRlciB0aGVtIGluIGJvdGggZGlyZWN0aW9ucwogIGNsX3JlZm9ybTAgPSBjbF9yZWZvcm1bY2xfcmVmb3JtJGRpcj09MCxdCiAgY2xfcmVmb3JtMCA9IGNsX3JlZm9ybTBbLGMoMSwzLDIsNSw0LDYsNyldCiAgY29sbmFtZXMoY2xfcmVmb3JtMCkgPSBjb2xuYW1lcyhjbF9yZWZvcm0pCiAgY2xfcmVmb3JtID0gcmJpbmQoY2xfcmVmb3JtLCBjbF9yZWZvcm0wKQogIAogIGRlY19jbCA9IGRlY19sW1tuXV0KICAKICBtYXRfbGlnX2NsID0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5yb3cgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRpZF9jcF9pbnRlcmFjdGlvbikpLCAKICAgICAgICAgICAgICAgICAgIG5jb2wgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRjdDEpKSkpCiAgbWF0X3JlY19jbCA9IGRhdGEuZnJhbWUobWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKHVuaXF1ZShjbF9yZWZvcm0kaWRfY3BfaW50ZXJhY3Rpb24pKSwgCiAgICAgICAgICAgICAgICAgICBuY29sID0gbGVuZ3RoKHVuaXF1ZShjbF9yZWZvcm0kY3QxKSkpKQogIGNvbG5hbWVzKG1hdF9saWdfY2wpID0gY29sbmFtZXMobWF0X3JlY19jbCkgPSB1bmlxdWUoY2xfcmVmb3JtJGN0MSkKICByb3duYW1lcyhtYXRfbGlnX2NsKSA9IHJvd25hbWVzKG1hdF9yZWNfY2wpID0gdW5pcXVlKGNsX3JlZm9ybSRpZF9jcF9pbnRlcmFjdGlvbikKICBmb3IoaSBpbiAxOm5yb3coY2xfcmVmb3JtKSl7CiAgICBnMSA9IGNsX3JlZm9ybVtpLCJscjEiXQogICAgZzIgPSBjbF9yZWZvcm1baSwibHIyIl0KICAgIAogICAgYzEgPSBjbF9yZWZvcm1baSwiY3QxIl0KICAgIGMyID0gY2xfcmVmb3JtW2ksImN0MiJdCiAgICAKICAgIGludCA9IGFzLmNoYXJhY3RlcihjbF9yZWZvcm1baSwxXSkKICAgIAogICAgbTEgPSBtZWFuKGRlY19jbFtkZWNfY2wkaWRfY3BfaW50ZXJhY3Rpb249PWludCAmIGRlY19jbFssMV09PWcxLGMxXSwgbmEucm0gPSBUKQogICAgaWYoaXMubmEobTEpKSBtMSA9IG1lYW4oZGVjX2NsW2RlY19jbCRpZF9jcF9pbnRlcmFjdGlvbj09aW50ICYgZGVjX2NsWyw1XT09ZzEsYzFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVCkKICAgIG1hdF9saWdfY2xbaW50LCBjMV0gPSBtMQogICAgCiAgICBtMiA9IG1lYW4oZGVjX2NsW2RlY19jbCRpZF9jcF9pbnRlcmFjdGlvbj09aW50ICYgZGVjX2NsWywxXT09ZzIsYzJdLCBuYS5ybSA9IFQpCiAgICBpZihpcy5uYShtMikpIG0yID0gbWVhbihkZWNfY2xbZGVjX2NsJGlkX2NwX2ludGVyYWN0aW9uPT1pbnQgJiBkZWNfY2xbLDVdPT1nMixjMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFQpCiAgICBpZihpcy5uYShtMSkgfCBpcy5uYShtMikpIHByaW50KGludCkKICAgIG1hdF9yZWNfY2xbaW50LCBjMl0gPSBtMgogIH0KICAKICAjIGxpZ2FuZCB2cyByZWNlcHRvciBjb3JyZWxhdGlvbgogIGNvcl9tYXRfZWFjaF9jbCA9IGNvcihtYXRfbGlnX2NsLCBtYXRfcmVjX2NsLCB1c2U9InBhaXJ3aXNlLmNvbXBsZXRlLm9icyIsIG1ldGhvZCA9ICJzcCIpCiAgCiAgc2NvcmluZ19tYXRzW1tuXV0gPSBsaXN0KCJtYXRfbGlnX2N0IiA9IG1hdF9saWdfY2wsICJtYXRfcmVjX2N0IiA9IG1hdF9yZWNfY2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb3JfbWF0X2VhY2giID0gY29yX21hdF9lYWNoX2NsKQogIAogICMgc3VtKGxpZywgcmVnKSBjb3JyZWxhdGlvbgogIG1hdF9saWdfY2xbaXMubmEobWF0X2xpZ19jbCldID0gMAogIG1hdF9yZWNfY2xbaXMubmEobWF0X3JlY19jbCldID0gMAogIG1hdF9ib3RoX2NsID0gbWF0X2xpZ19jbCttYXRfcmVjX2NsCiAgbWF0X2JvdGhfY2xbbWF0X2JvdGhfY2w9PTBdID0gTkEKICBjb3JfbWF0X2JvdGhfY2wgPSBjb3IobWF0X2JvdGhfY2wsIHVzZT0icGFpcndpc2UuY29tcGxldGUub2JzIiwgbWV0aG9kID0gInNwIikKICAKICBzY29yaW5nX21hdHNbW25dXSRjb3JfbWF0X2JvdGggPSBjb3JfbWF0X2JvdGhfY2wKfQpzYXZlUkRTKHNjb3JpbmdfbWF0cywgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS92My9zY29yaW5nX21hdHMuUkRTIikKYGBgCgpNYWtpbmcgdHdvIGNlbGwgdHlwZSBieSBpbnRlcmFjdGlvbiBtYXRyaWNlczogb25lIGhhcyB0aGUgbGlnYW5kIG1lYW4sIGFub3RoZXIgdGhlIHJlY2VwdG9yIG1lYW4gKGhlcmUgb25seSBkaXJlY3Rpb25hbCkKCmBgYHtyfQpzY29yaW5nX21hdHNfZGlyID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKHJlZm9ybV9saXN0KSl7CiAgY2xfcmVmb3JtID0gcmVmb3JtX2xpc3RbW25dXQogICMgYWRkIHJldmVyc2VkIG5vbi1kaXJlY3RlZCBpbnRlcmFjdGlvbnMsIHRvIGNvbnNpZGVyIHRoZW0gaW4gYm90aCBkaXJlY3Rpb25zCiAgY2xfcmVmb3JtMCA9IGNsX3JlZm9ybVtjbF9yZWZvcm0kZGlyPT0wLF0KICBjbF9yZWZvcm0wID0gY2xfcmVmb3JtMFssYygxLDMsMiw1LDQsNiw3KV0KICBjb2xuYW1lcyhjbF9yZWZvcm0wKSA9IGNvbG5hbWVzKGNsX3JlZm9ybSkKICBjbF9yZWZvcm0gPSByYmluZChjbF9yZWZvcm0sIGNsX3JlZm9ybTApCiAgCiAgY2xfcmVmb3JtID0gY2xfcmVmb3JtW2NsX3JlZm9ybSRkaXI9PTEsXQogIAogIGRlY19jbCA9IGRlY19sW1tuXV0KICAKICBtYXRfbGlnX2NsID0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5yb3cgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRpZF9jcF9pbnRlcmFjdGlvbikpLCAKICAgICAgICAgICAgICAgICAgIG5jb2wgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRjdDEpKSkpCiAgbWF0X3JlY19jbCA9IGRhdGEuZnJhbWUobWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKHVuaXF1ZShjbF9yZWZvcm0kaWRfY3BfaW50ZXJhY3Rpb24pKSwgCiAgICAgICAgICAgICAgICAgICBuY29sID0gbGVuZ3RoKHVuaXF1ZShjbF9yZWZvcm0kY3QxKSkpKQogIGNvbG5hbWVzKG1hdF9saWdfY2wpID0gY29sbmFtZXMobWF0X3JlY19jbCkgPSB1bmlxdWUoY2xfcmVmb3JtJGN0MSkKICByb3duYW1lcyhtYXRfbGlnX2NsKSA9IHJvd25hbWVzKG1hdF9yZWNfY2wpID0gdW5pcXVlKGNsX3JlZm9ybSRpZF9jcF9pbnRlcmFjdGlvbikKICBmb3IoaSBpbiAxOm5yb3coY2xfcmVmb3JtKSl7CiAgICBnMSA9IGNsX3JlZm9ybVtpLCJscjEiXQogICAgZzIgPSBjbF9yZWZvcm1baSwibHIyIl0KICAgIAogICAgYzEgPSBjbF9yZWZvcm1baSwiY3QxIl0KICAgIGMyID0gY2xfcmVmb3JtW2ksImN0MiJdCiAgICAKICAgIGludCA9IGFzLmNoYXJhY3RlcihjbF9yZWZvcm1baSwxXSkKICAgIAogICAgbTEgPSBtZWFuKGRlY19jbFtkZWNfY2wkaWRfY3BfaW50ZXJhY3Rpb249PWludCAmIGRlY19jbFssMV09PWcxLGMxXSwgbmEucm0gPSBUKQogICAgaWYoaXMubmEobTEpKSBtMSA9IG1lYW4oZGVjX2NsW2RlY19jbCRpZF9jcF9pbnRlcmFjdGlvbj09aW50ICYgZGVjX2NsWyw1XT09ZzEsYzFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVCkKICAgIG1hdF9saWdfY2xbaW50LCBjMV0gPSBtMQogICAgCiAgICBtMiA9IG1lYW4oZGVjX2NsW2RlY19jbCRpZF9jcF9pbnRlcmFjdGlvbj09aW50ICYgZGVjX2NsWywxXT09ZzIsYzJdLCBuYS5ybSA9IFQpCiAgICBpZihpcy5uYShtMikpIG0yID0gbWVhbihkZWNfY2xbZGVjX2NsJGlkX2NwX2ludGVyYWN0aW9uPT1pbnQgJiBkZWNfY2xbLDVdPT1nMixjMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFQpCiAgICBpZihpcy5uYShtMSkgfCBpcy5uYShtMikpIHByaW50KGludCkKICAgIG1hdF9yZWNfY2xbaW50LCBjMl0gPSBtMgogIH0KICAKICAjIGxpZ2FuZCB2cyByZWNlcHRvciBjb3JyZWxhdGlvbgogIGNvcl9tYXRfZWFjaF9jbCA9IGNvcihtYXRfbGlnX2NsLCBtYXRfcmVjX2NsLCB1c2U9InBhaXJ3aXNlLmNvbXBsZXRlLm9icyIsIG1ldGhvZCA9ICJzcCIpCiAgCiAgc2NvcmluZ19tYXRzX2Rpcltbbl1dID0gbGlzdCgibWF0X2xpZ19jdCIgPSBtYXRfbGlnX2NsLCAibWF0X3JlY19jdCIgPSBtYXRfcmVjX2NsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvcl9tYXRfZWFjaCIgPSBjb3JfbWF0X2VhY2hfY2wpCiAgCiAgIyBzdW0obGlnLCByZWcpIGNvcnJlbGF0aW9uCiAgbWF0X2xpZ19jbFtpcy5uYShtYXRfbGlnX2NsKV0gPSAwCiAgbWF0X3JlY19jbFtpcy5uYShtYXRfcmVjX2NsKV0gPSAwCiAgbWF0X2JvdGhfY2wgPSBtYXRfbGlnX2NsK21hdF9yZWNfY2wKICBtYXRfYm90aF9jbFttYXRfYm90aF9jbD09MF0gPSBOQQogIGNvcl9tYXRfYm90aF9jbCA9IGNvcihtYXRfYm90aF9jbCwgdXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiLCBtZXRob2QgPSAic3AiKQogIAogIHNjb3JpbmdfbWF0c19kaXJbW25dXSRjb3JfbWF0X2JvdGggPSBjb3JfbWF0X2JvdGhfY2wKfQpzYXZlUkRTKHNjb3JpbmdfbWF0c19kaXIsIGZpbGUgPSAicmVzdWx0cy9jZWxsX2NvbW0vdjMvc2NvcmluZ19tYXRzX2Rpci5SRFMiKQpgYGAKCkNvdW50IGludGVyYWN0aW9ucyBvZiBlYWNoIHR5cGUKCmBgYHtyfQppbnRlcl9jb3VudHNfZGlyID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKHJlZm9ybV9saXN0KSl7CiAgaW50ZXJfY291bnRzX2Rpcltbbl1dID0gbGlzdCgpCiAgZm9yKGkgaW4gYygwLDEpKXsKICAgIGNsX3JlZm9ybSA9IHJlZm9ybV9saXN0W1tuXV0KICAgIGNsX3JlZm9ybSA9IGNsX3JlZm9ybVtjbF9yZWZvcm0kZGlyPT1pLF0KICAgIAogICAgbl9kaXJfaW50ID0gbWF0cml4KDAsIG5yb3cgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRjdDEpKSwKICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gbGVuZ3RoKHVuaXF1ZShjbF9yZWZvcm0kY3QxKSkpCiAgICByb3duYW1lcyhuX2Rpcl9pbnQpID0gY29sbmFtZXMobl9kaXJfaW50KSA9IHVuaXF1ZShjbF9yZWZvcm0kY3QxKQogICAgZm9yKGMxIGluIHVuaXF1ZShjbF9yZWZvcm0kY3QxKSl7CiAgICAgIGZvcihjMiBpbiB1bmlxdWUoY2xfcmVmb3JtJGN0MSkpewogICAgICAgIG5fZGlyX2ludFtjMSxjMl0gPSBucm93KGNsX3JlZm9ybVtjbF9yZWZvcm0kY3QxPT1jMSAmIGNsX3JlZm9ybSRjdDI9PWMyLF0pCiAgICAgICAgaWYoaT09MCl7CiAgICAgICAgICBuX2Rpcl9pbnRbYzEsYzJdID0gbl9kaXJfaW50W2MxLGMyXStucm93KGNsX3JlZm9ybVtjbF9yZWZvcm0kY3QxPT1jMiAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsX3JlZm9ybSRjdDI9PWMxLF0pCiAgICAgICAgfQogICAgICB9CiAgICB9CiAgICBpbnRlcl9jb3VudHNfZGlyW1tuXV1bW2FzLmNoYXJhY3RlcihpKV1dID0gbl9kaXJfaW50CiAgfQp9CnNhdmVSRFMoaW50ZXJfY291bnRzX2RpciwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS92My9pbnRlcl9jb3VudHNfZGlyLlJEUyIpCmBgYAoKCgojIEludGVyYWN0aW9uIGFuYWx5c2lzIGluIGNvbmRpdGlvbnMKQ29tcGFyZSBpbnRlcmFjdGlvbnMgd2l0aCBERSBnZW5lcwoKYGBge3J9CiMgYWRkIGdlbmVzIGZyb20gY29tcGxleGVzCmNvbXBsZXhfcmVmID0gdW5pcXVlKHJiaW5kKGRlY19sJGhlYWx0aHlbZGVjX2wkaGVhbHRoeSRpc19jb21wbGV4PT0iVHJ1ZSIsYygxLDUpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY19sJGVtYm9saXNlZFtkZWNfbCRlbWJvbGlzZWQkaXNfY29tcGxleD09IlRydWUiLGMoMSw1KV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY19sJHJlZ2VuZXJhdGluZ1tkZWNfbCRyZWdlbmVyYXRpbmckaXNfY29tcGxleD09IlRydWUiLGMoMSw1KV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY19sJGhlYWx0aHlfc2ltcFtkZWNfbCRoZWFsdGh5X3NpbXAkaXNfY29tcGxleD09IlRydWUiLGMoMSw1KV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY19sJGVtYm9saXNlZF9zaW1wW2RlY19sJGVtYm9saXNlZF9zaW1wJGlzX2NvbXBsZXg9PSJUcnVlIixjKDEsNSldLAogICAgICAgICAgICAgICAgICAgICAgICAgICBkZWNfbCRyZWdlbmVyYXRpbmdfc2ltcFtkZWNfbCRyZWdlbmVyYXRpbmdfc2ltcCRpc19jb21wbGV4PT0iVHJ1ZSIsYygxLDUpXSkpCmludGVyX2RmID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKHJlZm9ybV9saXN0KSl7CiAgdG1wID0gbWVyZ2UocmVmb3JtX2xpc3RbW25dXSwgY29tcGxleF9yZWYsIGJ5LnggPSA0LCBieS55ID0gMiwgYWxsLnggPSBUKVssYygyLDMsNCwxLDUsOCw2LDcpXQogIGludGVyX2RmW1tuXV0gPSBtZXJnZSh0bXAsIGNvbXBsZXhfcmVmLCBieS54ID0gNSwgYnkueSA9IDIsIGFsbC54ID0gVClbLGMoMiwzLDQsNSw2LDEsOSw3LDgpXQogIGludGVyX2RmW1tuXV0kZ2VuZV9uYW1lLnhbaXMubmEoaW50ZXJfZGZbW25dXSRnZW5lX25hbWUueCldID0gaW50ZXJfZGZbW25dXSRscjFbaXMubmEoaW50ZXJfZGZbW25dXSRnZW5lX25hbWUueCldCiAgaW50ZXJfZGZbW25dXSRnZW5lX25hbWUueVtpcy5uYShpbnRlcl9kZltbbl1dJGdlbmVfbmFtZS55KV0gPSBpbnRlcl9kZltbbl1dJGxyMltpcy5uYShpbnRlcl9kZltbbl1dJGdlbmVfbmFtZS55KV0KICBjb2xuYW1lcyhpbnRlcl9kZltbbl1dKVtjKDUsNyldID0gYygiZ24xIiwgImduMiIpCn0KCmZvcihjYyBpbiBuYW1lcyhpbnRlcl9kZikpewogICMgaW50ZXJhY3Rpb24gdW5pcXVlIGluIGNvbmRpdGlvbgogIHVuaXF1ZV9pbnRlciA9IHNldGRpZmYodW5pcXVlKGludGVyX2RmW1tjY11dJGlkX2NwX2ludGVyYWN0aW9uKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShjKGludGVyX2RmW1tuYW1lcyhpbnRlcl9kZilbbmFtZXMoaW50ZXJfZGYpIT1jY11bMV1dXSRpZF9jcF9pbnRlcmFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyX2RmW1tuYW1lcyhpbnRlcl9kZilbbmFtZXMoaW50ZXJfZGYpIT1jY11bMl1dXSRpZF9jcF9pbnRlcmFjdGlvbikpKQogIGludGVyX2RmW1tjY11dJGNwZGJfdW5pcXVlID0gaW50ZXJfZGZbW2NjXV0kaWRfY3BfaW50ZXJhY3Rpb24gJWluJSB1bmlxdWVfaW50ZXIKICAKICAjIGxyMSB1bmlxdWUgaW4gY29uZGl0aW9uCiAgdW5pcXVlX2xyMSA9IHNldGRpZmYodW5pcXVlKGludGVyX2RmW1tjY11dJGxyMSksCiAgICAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoYyhpbnRlcl9kZltbbmFtZXMoaW50ZXJfZGYpW25hbWVzKGludGVyX2RmKSE9Y2NdWzFdXV0kbHIxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGZbW25hbWVzKGludGVyX2RmKVtuYW1lcyhpbnRlcl9kZikhPWNjXVsyXV1dJGxyMSkpKQogIGludGVyX2RmW1tjY11dJGNwZGJfdW5pcXVlX2xyMSA9IGludGVyX2RmW1tjY11dJGxyMSAlaW4lIHVuaXF1ZV9scjEKICAKICAjIGxyMiB1bmlxdWUgaW4gY29uZGl0aW9uCiAgdW5pcXVlX2xyMiA9IHNldGRpZmYodW5pcXVlKGludGVyX2RmW1tjY11dJGxyMiksCiAgICAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoYyhpbnRlcl9kZltbbmFtZXMoaW50ZXJfZGYpW25hbWVzKGludGVyX2RmKSE9Y2NdWzFdXV0kbHIyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGZbW25hbWVzKGludGVyX2RmKVtuYW1lcyhpbnRlcl9kZikhPWNjXVsyXV1dJGxyMikpKQogIGludGVyX2RmW1tjY11dJGNwZGJfdW5pcXVlX2xyMiA9IGludGVyX2RmW1tjY11dJGxyMiAlaW4lIHVuaXF1ZV9scjIKfQpmb3IoY2MgaW4gbmFtZXMoaW50ZXJfZGYpKXsKICBvYzEgPSBuYW1lcyhpbnRlcl9kZilbbmFtZXMoaW50ZXJfZGYpIT1jY11bMV0KICBvYzIgPSBuYW1lcyhpbnRlcl9kZilbbmFtZXMoaW50ZXJfZGYpIT1jY11bMl0KICBpbnQxID0gcGFzdGUwKGludGVyX2RmW1tjY11dJGlkX2NwX2ludGVyYWN0aW9uLGludGVyX2RmW1tjY11dJGN0MSxpbnRlcl9kZltbY2NdXSRjdDIpCiAgaW50MiA9IHVuaXF1ZShwYXN0ZTAoaW50ZXJfZGZbW29jMV1dJGlkX2NwX2ludGVyYWN0aW9uLGludGVyX2RmW1tvYzFdXSRjdDEsaW50ZXJfZGZbW29jMV1dJGN0MikpCiAgaW50MyA9IHVuaXF1ZShwYXN0ZTAoaW50ZXJfZGZbW29jMl1dJGlkX2NwX2ludGVyYWN0aW9uLGludGVyX2RmW1tvYzJdXSRjdDEsaW50ZXJfZGZbW29jMl1dJGN0MikpCiAgdW5pcXVlX2ludGVyID0gc2V0ZGlmZih1bmlxdWUoaW50MSksIHVuaXF1ZShjKGludDIsIGludDMpKSkKICBpbnRlcl9kZltbY2NdXSRjcGRiX3VuaXF1ZV9jdCA9IGludDEgJWluJSB1bmlxdWVfaW50ZXIKfQpmb3IobiBpbiBuYW1lcyhpbnRlcl9kZikpewogIGRmID0gaW50ZXJfZGZbW25dXQogIGRmID0gdW5pcXVlKGRmWyxjKDE6MyldKQogIAogIGludHNfdW5fY3QgPSByb3dTdW1zKHRhYmxlKGRmJGlkX2NwX2ludGVyYWN0aW9uLCBkZiRjdDEpPjApPDQgfCAKICAgIHJvd1N1bXModGFibGUoZGYkaWRfY3BfaW50ZXJhY3Rpb24sIGRmJGN0Mik+MCk8NAogIAogIGludGVyX2RmW1tuXV0kaW50ZXJfY3Rfc3BlYyA9IGludGVyX2RmW1tuXV0kaWRfY3BfaW50ZXJhY3Rpb24gJWluJSBuYW1lcyhpbnRzX3VuX2N0KVtpbnRzX3VuX2N0XQp9CmZvcihuIGluIG5hbWVzKGludGVyX2RmKSl7CiAgeHh4ID0gZGF0YS5mcmFtZShjdCA9IGMoaW50ZXJfZGZbW25dXVssMl0sIGludGVyX2RmW1tuXV1bLDNdKSwgCiAgICAgICAgICAgICAgICAgICBsciA9IGMoaW50ZXJfZGZbW25dXVssNF0sIGludGVyX2RmW1tuXV1bLDZdKSkKICB4eHggPSB1bmlxdWUoeHh4KQogIHRhYl9sciA9ICh0YWJsZSh4eHgkbHIsIHh4eCRjdCk+MCkqMQogIHRhYl9sciA9IHJvd1N1bXModGFiX2xyKQogIAogIGludGVyX2RmW1tuXV0kbHIxX25jdCA9IHRhYl9scltpbnRlcl9kZltbbl1dJGxyMV0KICBpbnRlcl9kZltbbl1dJGxyMl9uY3QgPSB0YWJfbHJbaW50ZXJfZGZbW25dXSRscjJdCn0Kc2F2ZVJEUyhpbnRlcl9kZiwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS92My9jb25kX2RpZmZfaW50ZXJhY3RfREUuUkRTIikKYGBgCgpQbG90IGludGVyYWN0aW9uIGhlYXRtYXAgLSB0b3RhbCBhbmQgZmlsdGVyZWQKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CmludF9jb3VudHNfdG90YWwgPSBsaXN0KCkKaW50X2NvdW50c19maWx0ID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKGludGVyX2RmKSl7CiAgc3ViZGZjdCA9IHVuaXF1ZShpbnRlcl9kZltbbl1dWywxOjNdKQogIHN1YmRmY3QgPSB1bmlxdWUoc3ViZGZjdCkKICBkZmN0ID0gZGF0YS5mcmFtZSgiY3QxIiA9IGMoc3ViZGZjdCRjdDEsIHN1YmRmY3QkY3QyKSwKICAgICAgICAgICAgICAgICAgICAiY3QyIiA9IGMoc3ViZGZjdCRjdDIsIHN1YmRmY3QkY3QxKSkKICBpbnRfY291bnRzX3RvdGFsW1tuXV0gPSBkZmN0CiAgcGhlYXRtYXA6OnBoZWF0bWFwKHRhYmxlKGRmY3QkY3QxLCBkZmN0JGN0MiksIG1haW4gPSBuKQogIAogIGtlZXAgPSBpbnRlcl9kZltbbl1dJGxyMV9uY3Q8PTEgfCBpbnRlcl9kZltbbl1dJGxyMl9uY3Q8PTEKICBzdWJkZmN0ID0gdW5pcXVlKGludGVyX2RmW1tuXV1ba2VlcCxjKDE6MywgMTUsMTYpXSkKICBzd3AgPSBzdWJkZmN0Wyw1XT09MQogIHRtcCA9IHN1YmRmY3QkY3QyW3N3cF0KICBzdWJkZmN0JGN0Mltzd3BdID0gc3ViZGZjdCRjdDFbc3dwXQogIHN1YmRmY3QkY3QxW3N3cF0gPSB0bXAKICB0bXAgPSBzdWJkZmN0JGxyMl9uY3Rbc3dwXQogIHN1YmRmY3QkbHIyX25jdFtzd3BdID0gc3ViZGZjdCRscjFfbmN0W3N3cF0KICBzdWJkZmN0JGxyMV9uY3Rbc3dwXSA9IHRtcAogIGR1cCA9IHN1YmRmY3Rbc3ViZGZjdCRscjFfbmN0PT0xICYgc3ViZGZjdCRscjJfbmN0PT0xLF0KICB0bXAgPSBkdXAkY3QyCiAgZHVwJGN0MiA9IGR1cCRjdDEKICBkdXAkY3QxID0gdG1wCiAgc3ViZGZjdCA9IHJiaW5kKHN1YmRmY3QsIGR1cCkKICBzdWJkZmN0ID0gdW5pcXVlKHN1YmRmY3RbLDE6M10pCiAgaW50X2NvdW50c19maWx0W1tuXV0gPSBkZmN0CiAgcGhlYXRtYXA6OnBoZWF0bWFwKHRhYmxlKHN1YmRmY3QkY3QxLCBzdWJkZmN0JGN0MiksIG1haW4gPSBuKQp9CnNhdmVSRFMoaW50X2NvdW50c190b3RhbCwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS92My9pbnRfY291bnRzX3RvdGFsLlJEUyIpCnNhdmVSRFMoaW50X2NvdW50c19maWx0LCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3YzL2ludF9jb3VudHNfZmlsdC5SRFMiKQpgYGAKCkdldCBub24tdWJpcXVpdG91cyBpbnRlcmFjdGlvbnMKCmBgYHtyfQppbnRlcl9ub25zcyA9IHNldGRpZmYodW5pcXVlKGMoaW50ZXJfZGYkZW1ib2xpc2VkJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGYkcmVnZW5lcmF0aW5nJGlkX2NwX2ludGVyYWN0aW9uKSksCiAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbikpCmludGVyX25vbmVtYiA9IHNldGRpZmYodW5pcXVlKGMoaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyX2RmJHJlZ2VuZXJhdGluZyRpZF9jcF9pbnRlcmFjdGlvbikpLAogICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKGludGVyX2RmJGVtYm9saXNlZCRpZF9jcF9pbnRlcmFjdGlvbikpCmludGVyX25vbnJlZyA9IHNldGRpZmYodW5pcXVlKGMoaW50ZXJfZGYkZW1ib2xpc2VkJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbikpLAogICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKGludGVyX2RmJHJlZ2VuZXJhdGluZyRpZF9jcF9pbnRlcmFjdGlvbikpCgpjdF9nX2NvbmQgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMoaW50ZXJfZGYpWzE6M10pewogIGRmID0gaW50ZXJfZGZbW25dXVtpbnRlcl9kZltbbl1dJGNwZGJfdW5pcXVlIHwgCiAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGZbW25dXSRpZF9jcF9pbnRlcmFjdGlvbiAlaW4lIGMoaW50ZXJfbm9uc3MsIGludGVyX25vbmVtYiwgaW50ZXJfbm9ucmVnKSxdCiAgI2RmID0gZGZbZGYkbHIxX25jdDw9MSB8IGRmJGxyMl9uY3Q8PTEsXQogIAogIGN0cyA9IHVuaXF1ZShjKGRmJGN0MSwgZGYkY3QyKSkKICBjdF9nX2xpc3QgPSBsaXN0KCkKICBmb3IoY3QgaW4gY3RzKXsKICAgIGN0X2dfbGlzdFtbY3RdXSA9IGRhdGEuZnJhbWUoImludGVyYWN0IiA9IGMoYXMuY2hhcmFjdGVyKGRmJGlkX2NwX2ludGVyYWN0aW9uW2RmJGN0MT09Y3RdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGRmJGlkX2NwX2ludGVyYWN0aW9uW2RmJGN0Mj09Y3RdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnZW5lIiA9IGMoYXMuY2hhcmFjdGVyKGRmJGduMVtkZiRjdDE9PWN0XSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGRmJGduMltkZiRjdDI9PWN0XSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ2VuZV90YXJnZXQiID0gYyhhcy5jaGFyYWN0ZXIoZGYkZ24yW2RmJGN0MT09Y3RdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGRmJGduMVtkZiRjdDI9PWN0XSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3QiID0gY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdF90YXJnZXQiID0gYyhhcy5jaGFyYWN0ZXIoZGYkY3QyW2RmJGN0MT09Y3RdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGRmJGN0MVtkZiRjdDI9PWN0XSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29uZCIgPSBuLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKICB9CiAgY3RfZ19jb25kW1tuXV0gPSB1bmlxdWUoUmVkdWNlKHJiaW5kLCBjdF9nX2xpc3QpKQp9CmN0X2dfY29uZCA9IFJlZHVjZShyYmluZCwgY3RfZ19jb25kKQpkaW0oY3RfZ19jb25kKQpsZW5ndGgodW5pcXVlKGN0X2dfY29uZCRpbnRlcmFjdCkpCgoKaW50ZXJfbm9uc3MgPSBzZXRkaWZmKHVuaXF1ZShjKGludGVyX2RmJGVtYm9saXNlZF9zaW1wJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGYkcmVnZW5lcmF0aW5nX3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24pKSwKICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShpbnRlcl9kZiRoZWFsdGh5X3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24pKQppbnRlcl9ub25lbWIgPSBzZXRkaWZmKHVuaXF1ZShjKGludGVyX2RmJGhlYWx0aHlfc2ltcCRpZF9jcF9pbnRlcmFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyX2RmJHJlZ2VuZXJhdGluZ19zaW1wJGlkX2NwX2ludGVyYWN0aW9uKSksCiAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoaW50ZXJfZGYkZW1ib2xpc2VkX3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24pKQppbnRlcl9ub25yZWcgPSBzZXRkaWZmKHVuaXF1ZShjKGludGVyX2RmJGVtYm9saXNlZF9zaW1wJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGYkaGVhbHRoeV9zaW1wJGlkX2NwX2ludGVyYWN0aW9uKSksCiAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoaW50ZXJfZGYkcmVnZW5lcmF0aW5nX3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24pKQoKY3RzX2dfY29uZCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhpbnRlcl9kZilbNDo2XSl7CiAgZGYgPSBpbnRlcl9kZltbbl1dW2ludGVyX2RmW1tuXV0kY3BkYl91bmlxdWUgfCAKICAgICAgICAgICAgICAgICAgICAgICBpbnRlcl9kZltbbl1dJGlkX2NwX2ludGVyYWN0aW9uICVpbiUgYyhpbnRlcl9ub25zcywgaW50ZXJfbm9uZW1iLCBpbnRlcl9ub25yZWcpLF0KICAjZGYgPSBkZltkZiRscjFfbmN0PD0xIHwgZGYkbHIyX25jdDw9MSxdCiAgCiAgY3RzID0gdW5pcXVlKGMoZGYkY3QxLCBkZiRjdDIpKQogIGN0X2dfbGlzdCA9IGxpc3QoKQogIGZvcihjdCBpbiBjdHMpewogICAgY3RfZ19saXN0W1tjdF1dID0gZGF0YS5mcmFtZSgiaW50ZXJhY3QiID0gYyhhcy5jaGFyYWN0ZXIoZGYkaWRfY3BfaW50ZXJhY3Rpb25bZGYkY3QxPT1jdF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkaWRfY3BfaW50ZXJhY3Rpb25bZGYkY3QyPT1jdF0pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdlbmUiID0gYyhhcy5jaGFyYWN0ZXIoZGYkZ24xW2RmJGN0MT09Y3RdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkZ24yW2RmJGN0Mj09Y3RdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnZW5lX3RhcmdldCIgPSBjKGFzLmNoYXJhY3RlcihkZiRnbjJbZGYkY3QxPT1jdF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkZ24xW2RmJGN0Mj09Y3RdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdCIgPSBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImN0X3RhcmdldCIgPSBjKGFzLmNoYXJhY3RlcihkZiRjdDJbZGYkY3QxPT1jdF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkY3QxW2RmJGN0Mj09Y3RdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb25kIiA9IG4sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIH0KICBjdHNfZ19jb25kW1tuXV0gPSB1bmlxdWUoUmVkdWNlKHJiaW5kLCBjdF9nX2xpc3QpKQp9CmN0c19nX2NvbmQgPSBSZWR1Y2UocmJpbmQsIGN0c19nX2NvbmQpCmRpbShjdHNfZ19jb25kKQpsZW5ndGgodW5pcXVlKGN0c19nX2NvbmQkaW50ZXJhY3QpKQoKY3RfZ19sID0gbGlzdCgiY3RzX2dfY29uZCIgPSBjdHNfZ19jb25kLAogICAgICAgICAgICAgICJjdF9nX2NvbmQiID0gY3RfZ19jb25kKQpgYGAKCkFubm90YXRlIGludGVyYWN0aW9ucwoKYGBge3J9CiN4eHggPSBtZXJnZSh1bmlxdWUocmJpbmQoY3RfZ19jb25kWywxOjNdLCBjdHNfZ19jb25kWywxOjNdKSksIAojICAgICAgICAgICAgdW5pcXVlKGludGVyX2Fubm90WyxjKDEsNCldKSwgYnkgPSAxLCBhbGwueCA9IFQpCiN3cml0ZS5jc3YoeHh4LCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3YzL2ludGVyX3VuaXF1ZTUuY3N2Iiwgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gVCwgcXVvdGUgPSBGKQppbnRlcl9hbm5vdCA9IHJlYWQuY3N2KCJyZXN1bHRzL2NlbGxfY29tbS91cGR0L2ludGVyX3VuaXF1ZTUuY3N2IiwgaGVhZGVyID0gVCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgppbnRlcl9hbm5vdCA9IHVuaXF1ZShpbnRlcl9hbm5vdFssYygxLDQpXSkKCmRlc2NyaXB0aW9uID0gc3Ryc3BsaXQoaW50ZXJfYW5ub3QkZGVzY3JpcHRpb24sICI7IikKaW50ZXJfZGVzID0gbGFwcGx5KDE6bGVuZ3RoKGRlc2NyaXB0aW9uKSwgCiAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSByZXAoaW50ZXJfYW5ub3QkaW50ZXJhY3RbeF0sIGxlbmd0aChkZXNjcmlwdGlvbltbeF1dKSkpCmludGVyX2Fubm90ID0gZGF0YS5mcmFtZSgiaW50ZXIiID0gdW5saXN0KGludGVyX2RlcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAiZnVuY3QiID0gdW5saXN0KGRlc2NyaXB0aW9uKSkKCmludGVyX2Fubm90JGZ1bmN0W2ludGVyX2Fubm90JGZ1bmN0PT0iaW50ZXJjZWxsdWxhciBhZGhlc2lvbiJdID0gImFkaGVzaW9uIgppbnRlcl9hbm5vdCRmdW5jdFtpbnRlcl9hbm5vdCRmdW5jdD09ImFudGlib2R5IHJlZ3VsYXRpb24iXSA9ICJpbW11bmUgcmVndWxhdGlvbiIKaW50ZXJfYW5ub3QkZnVuY3RbaW50ZXJfYW5ub3QkZnVuY3Q9PSJhbnRpZ2VuIHByZXNlbnRhdGlvbiJdID0gImltbXVuZSBhY3Rpdml0eSIKCndyaXRlLmNzdihpbnRlcl9hbm5vdCwgZmlsZSA9ICJkYXRhL2ludGVyYWN0aW9uX2Fubm90YXRpb24yLmNzdiIsIAogICAgICAgICAgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gVCwgcXVvdGUgPSBGKQpjb2xuYW1lcyhpbnRlcl9hbm5vdCkgPSBjKCJpbnRlcmFjdCIsICJkZXNjcmlwdGlvbiIpCgpjdF9nX2NvbmRfYW5uX2wgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMoY3RfZ19sKSl7CiAgY3RfZ19jb25kX2FubiA9IG1lcmdlKGN0X2dfbFtbbl1dLCBpbnRlcl9hbm5vdCwgYnkgPSAxLCBhbGwueCA9IFQpCiAgY3RfZ19jb25kX2FubiA9IHVuaXF1ZShjdF9nX2NvbmRfYW5uWyxjKDEsNCw1LDYsNyldKQogIAogIGN0X2dfY29uZF9hbm4gPSBkYXRhLmZyYW1lKCJpbnRlcmFjdGlvbnMiID0gcmVwKGFzLmNoYXJhY3RlcihjdF9nX2NvbmRfYW5uJGludGVyYWN0KSwyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2VsbHR5cGVzIiA9IGMoYXMuY2hhcmFjdGVyKGN0X2dfY29uZF9hbm4kY3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoY3RfZ19jb25kX2FubiRjdF90YXJnZXQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA9IHJlcChhcy5jaGFyYWN0ZXIoY3RfZ19jb25kX2FubiRjb25kKSwyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZGVzY3JpcHRpb24iID0gcmVwKGFzLmNoYXJhY3RlcihjdF9nX2NvbmRfYW5uJGRlc2NyaXB0aW9uKSwyKSkKICBjdF9nX2NvbmRfYW5uJGNvbmRpdGlvbiA9IGZhY3Rvcih1bmxpc3QobGFwcGx5KHN0cnNwbGl0KGN0X2dfY29uZF9hbm4kY29uZGl0aW9uLCAiXyIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHhbMV0pKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiaGVhbHRoeSIsICJlbWJvbGlzZWQiLCAicmVnZW5lcmF0aW5nIikpCiAgCiAgcGx0X2Jhcl9mdW5jID0gZ2dwbG90KGN0X2dfY29uZF9hbm4sIGFlcyh4ID0gY29uZGl0aW9uLCBmaWxsID0gY29uZGl0aW9uKSkrCiAgICBmYWNldF9ncmlkKGRlc2NyaXB0aW9ufmNlbGx0eXBlcywgc2NhbGVzID0gImZyZWVfeSIpKwogICAgZ2VvbV9iYXIoKSsKICAgIHRoZW1lX2J3KCkrCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMSksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgCiAgcGRmKHBhc3RlMCgicmVzdWx0cy9jZWxsX2NvbW0vdjMvIiwgbiwgIl9wbHRfYmFyX2Z1bmMucGRmIiksIGhlaWdodCA9IDIwLCB3aWR0aCA9IDUwKQogIHByaW50KHBsdF9iYXJfZnVuYykKICBkZXYub2ZmKCkKICAKICBjdF9nX2NvbmRfYW5uX2xbW25dXSA9IGN0X2dfY29uZF9hbm4KICBzYXZlUkRTKGN0X2dfY29uZF9hbm4sIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvY2VsbF9jb21tL3YzLyIsIG4sICJfYW5uLlJEUyIpKQp9CmBgYAoKR2V0IGV4cHJlc3Npb24gZm9yIGVhY2ggaW50ZXJhY3Rpb24gaW4gZWFjaCBjb25kaXRpb24KCmBgYHtyfQpleHBfbGlzdCA9IGxpc3QoKQppbnRfZ3JvdXBzID0gbGlzdCgiY3RzX2dfY29uZCIgPSB1bmlxdWUoYyhzaWdfbWVhbnNfbmFtZXNfbCRoZWFsdGh5X3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ19tZWFuc19uYW1lc19sJGVtYm9saXNlZF9zaW1wJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWdfbWVhbnNfbmFtZXNfbCRyZWdlbmVyYXRpbmdfc2ltcCRpZF9jcF9pbnRlcmFjdGlvbikpLAogICAgICAgICAgICAgICAgICAiY3RfZ19jb25kIiA9IHVuaXF1ZShjKHNpZ19tZWFuc19uYW1lc19sJGhlYWx0aHkkaWRfY3BfaW50ZXJhY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ19tZWFuc19uYW1lc19sJGVtYm9saXNlZCRpZF9jcF9pbnRlcmFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnX21lYW5zX25hbWVzX2wkcmVnZW5lcmF0aW5nJGlkX2NwX2ludGVyYWN0aW9uKSkpCmZvcihuIGluIG5hbWVzKHNpZ19tZWFuc19uYW1lc19sKSl7CiAgZGYgPSBzaWdfbWVhbnNfbmFtZXNfbFtbbl1dWyxjKDEsNTo2LDEzOm5jb2woc2lnX21lYW5zX25hbWVzX2xbW25dXSkpXQogIAogICNjb2xuYW1lcyhkZikgPSBnc3ViKCIuIiwgIiAiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJnZC5ULmNlbGxzIiwgImdkLVQgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJCLmNlbGxzIiwgIkIgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJEaXZpZGluZy5lbmRvdGhlbGlhbC5jZWxscyIsICJEaXZpZGluZyBlbmRvdGhlbGlhbCBjZWxscyIsIAogICAgICAgICAgICAgICAgICAgICAgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiRGl2aWRpbmcuY0RDcyIsICJEaXZpZGluZyBjRENzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiSW5maWx0cmF0aW5nLk5LLmNlbGxzIiwgIkluZmlsdHJhdGluZyBOSyBjZWxscyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk1hY3JvcGhhZ2VzLi5IRVM0Li4iLCAiTWFjcm9waGFnZXMgKEhFUzQrKSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoImFjdGl2YXRlZC5EQ3MiLCAiYWN0aXZhdGVkIERDcyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIlBlcmljZW50cmFsLkxTRUMiLCAiUGVyaWNlbnRyYWwgTFNFQyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk1pZHpvbmFsLkxTRUMiLCAiTWlkem9uYWwgTFNFQyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIlBlcmlwb3J0YWwuTFNFQyIsICJQZXJpcG9ydGFsIExTRUMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJQZXJpcG9ydGFsLkxTRUMiLCAiUGVyaXBvcnRhbCBMU0VDIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTksuY2VsbHMuMSIsICJOSyBjZWxscyAxIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTksuY2VsbHMuMiIsICJOSyBjZWxscyAyIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTksuY2VsbHMuMyIsICJOSyBjZWxscyAzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTksuY2VsbHMiLCAiTksgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJDRDguYWIuVC5jZWxscy4xIiwgIkNEOCBhYi1UIGNlbGxzIDEiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJDRDguYWIuVC5jZWxscy4yIiwgIkNEOCBhYi1UIGNlbGxzIDIiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJDRDguYWIuVC5jZWxscy4zIiwgIkNEOCBhYi1UIGNlbGxzIDMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJDRDguYWIuVC5jZWxscyIsICJDRDggYWItVCBjZWxscyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk5haXZlLkNENC4uVC5jZWxscyIsICJOYWl2ZSBDRDQrIFQgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJhYi5ULmNlbGxzLi5zdHJlc3MuIiwgImFiLVQgY2VsbHMgKHN0cmVzcykiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJhYi5ULmNlbGxzLi5zdHJlc3MuIiwgImFiLVQgY2VsbHMgKHN0cmVzcykiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJGaWJyb2JsYXN0cy4xIiwgIkZpYnJvYmxhc3RzIDEiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJGaWJyb2JsYXN0cy4yIiwgIkZpYnJvYmxhc3RzIDIiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJGaWJyb2JsYXN0cy4zIiwgIkZpYnJvYmxhc3RzIDMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJNb25vY3l0ZXMuLnNlY3JldG9yeS4iLCAiTW9ub2N5dGVzIChzZWNyZXRvcnkpIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTW9ub2N5dGVzLi5UUkVNMi4uQ0Q5Li4iLCAiTW9ub2N5dGVzIChUUkVNMisgQ0Q5KykiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJNb25vY3l0ZXMuLklHU0YyMS4uR1BSMzQuLiIsICJNb25vY3l0ZXMgKElHU0YyMSsgR1BSMzQrKSIsIAogICAgICAgICAgICAgICAgICAgICAgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTFNFQy4uc3RyZXNzLiIsICJMU0VDIChzdHJlc3MpIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTFNFQy4ucmVtb2RlbGxpbmcuIiwgIkxTRUMgKHJlbW9kZWxsaW5nKSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIkxTRUMuLmludGVyZmVyb24uIiwgIkxTRUMgKGludGVyZmVyb24pIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTFNFQy4uaGlnaC5NVC4yLiIsICJMU0VDIChoaWdoIE1UIDIpIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTFNFQy4uaGlnaC5NVC4xLiIsICJMU0VDIChoaWdoIE1UIDEpIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTFNFQy4uaGlnaC5NVC4iLCAiTFNFQyAoaGlnaCBNVCkiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJMU0VDLi5mZW5lc3RyLi4iLCAiTFNFQyAoZmVuZXN0ci4pIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiS3VwZmZlci5jZWxscy4uU1VDTlIxLi4iLCAiS3VwZmZlciBjZWxscyAoU1VDTlIxKykiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJJZ0cuLlBsYXNtYS5jZWxscyIsICJJZ0crIFBsYXNtYSBjZWxscyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIklnQS4uUGxhc21hLmNlbGxzIiwgIklnQSsgUGxhc21hIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiRUMubm9uLkxTRUMiLCAiRUMgbm9uLUxTRUMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJEaXZpZGluZy5ULk5LLmNlbGxzIiwgIkRpdmlkaW5nIFQvTksgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJLdXBmZmVyLmNlbGxzIiwgIkt1cGZmZXIgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJUUk0uY2VsbHMiLCAiVFJNIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTUFJVC5jZWxscy4xIiwgIk1BSVQgY2VsbHMgMSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk1BSVQuY2VsbHMuMiIsICJNQUlUIGNlbGxzIDIiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJNQUlULmNlbGxzIiwgIk1BSVQgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJMeW1waGF0aWMuRUMiLCAiTHltcGhhdGljIEVDIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiU3RlbGxhdGUuY2VsbHMiLCAiU3RlbGxhdGUgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICAjIGhlYWQoY29sbmFtZXMoZGYpLCA2MCkKICAKICBkZiA9IHJlc2hhcGUyOjptZWx0KGRmLCBpZC52YXJzID0gMTozKQogIGRmJHZhbHVlW2lzLm5hKGRmJHZhbHVlKV0gPSAwCiAgZGYgPSBkZiAlPiUKICAgZ3JvdXBfYnkoaWRfY3BfaW50ZXJhY3Rpb24sIGdlbmVfYSwgZ2VuZV9iLCB2YXJpYWJsZSkgJT4lCiAgIHN1bW1hcmlzZSh2YWx1ZT0obWVhbih2YWx1ZSkpLCAuZ3JvdXBzID0gImtlZXAiKQogIGRmID0gYXMuZGF0YS5mcmFtZShkZikKICAKICBucyA9IGlmKGdyZXBsKCJzaW1wIiwgbikpICJjdHNfZ19jb25kIiBlbHNlICJjdF9nX2NvbmQiCiAgY3RfZ19jb25kX2FubiA9IGN0X2dfY29uZF9hbm5fbFtbbnNdXQogIHN1Yl9kZiA9IGRmW2RmJGlkX2NwX2ludGVyYWN0aW9uICVpbiUgY3RfZ19jb25kX2FubiRpbnRlcmFjdGlvbnMsXQogIHN1Yl9kZiA9IG1lcmdlKGN0X2dfbFtbbnNdXSwgc3ViX2RmLCBhbGwgPSBULCBieSA9IDEpCiAgc3ViX2RmID0gc3ViX2RmW3N1Yl9kZiRpbnRlcmFjdCAlaW4lIGludF9ncm91cHNbW25zXV0sXQogIHN1Yl9kZiR2YXJpYWJsZSA9IGFzLmNoYXJhY3RlcihzdWJfZGYkdmFyaWFibGUpCiAgc3ViX2RmJHZhbHVlW2lzLm5hKHN1Yl9kZiR2YWx1ZSldID0gMAogIAogIGN0MSA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQoc3ViX2RmJHZhcmlhYmxlLCAiLiIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHhbMV0pKQogIGN0MiA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQoc3ViX2RmJHZhcmlhYmxlLCAiLiIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHhbMl0pKQogIGtlZXAgPSBzYXBwbHkoMTpucm93KHN1Yl9kZiksIGZ1bmN0aW9uKHgpIChzdWJfZGYkY3RbeF09PWN0MVt4XSAmIHN1Yl9kZiRjdF90YXJnZXRbeF09PWN0Mlt4XSkgfAogICAgICAgICAgICAgICAgICAoc3ViX2RmJGN0W3hdPT1jdDJbeF0gJiBzdWJfZGYkY3RfdGFyZ2V0W3hdPT1jdDFbeF0pKQogIGV4cF9saXN0W1tuXV0gPSBzdWJfZGZba2VlcCxjKDE6NiwxMCldCn0KZm9yKG4gaW4gbmFtZXMoZXhwX2xpc3QpKXsKICBleHBfbGlzdFtbbl1dID0gZXhwX2xpc3RbW25dXVshaXMubmEoZXhwX2xpc3RbW25dXSRpbnRlcmFjdCksXQp9CgpjdF9pbnRfZXhwX2wgPSBsaXN0KCkKZm9yKHNpbXAgaW4gYyhULCBGKSl7CiAgbl91c2UgPSBuYW1lcyhleHBfbGlzdClbZ3JlcGwoInNpbXAiLCBuYW1lcyhleHBfbGlzdCkpPT1zaW1wXQogICNjdF9pbnRfZXhwID0gY2JpbmQoZXhwX2xpc3RbW25fdXNlWzFdXV0sIGV4cF9saXN0W1tuX3VzZVszXV1dJHZhbHVlLCBleHBfbGlzdFtbbl91c2VbM11dXSR2YWx1ZSkKICBjdF9pbnRfZXhwID0gbWVyZ2UoZXhwX2xpc3RbW25fdXNlWzFdXV0sIGV4cF9saXN0W1tuX3VzZVsyXV1dLCBieSA9IDE6NikKICBjdF9pbnRfZXhwID0gbWVyZ2UoY3RfaW50X2V4cCwgZXhwX2xpc3RbW25fdXNlWzNdXV0sIGJ5ID0gMTo2KQogIGN0X2ludF9leHAkdmFsdWUueFtpcy5uYShjdF9pbnRfZXhwJHZhbHVlLngpXSA9IGN0X2ludF9leHAkdmFsdWUueVtpcy5uYShjdF9pbnRfZXhwJHZhbHVlLnkpXSA9IGN0X2ludF9leHAkdmFsdWVbaXMubmEoY3RfaW50X2V4cCR2YWx1ZSldID0gMAogIGNvbG5hbWVzKGN0X2ludF9leHApWzc6OV0gPSBjKCJoZWFsdGh5X2V4cCIsICJlbWJvbGlzZWRfZXhwIiwgInJlZ2VuZXJhdGluZ19leHAiKQogIGN0X2ludF9leHAgPSBjdF9pbnRfZXhwW2N0X2ludF9leHAkaGVhbHRoeV9leHA+MCB8IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY3RfaW50X2V4cCRlbWJvbGlzZWRfZXhwPjAgfCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN0X2ludF9leHAkcmVnZW5lcmF0aW5nX2V4cD4wLF0KCiAgdHVwX2xpc3QgPSBsaXN0KCkKICBrZWVwX3JvdyA9IGMoKQogIGZvcihpIGluIDE6bnJvdyhjdF9pbnRfZXhwKSl7CiAgICB0dXAxID0gcGFzdGUoYyhjdF9pbnRfZXhwJGdlbmVbaV0sIGN0X2ludF9leHAkZ2VuZV90YXJnZXRbaV0sIGN0X2ludF9leHAkY3RbaV0sIAogICAgICAgICAgICAgICAgICAgY3RfaW50X2V4cCRjdF90YXJnZXRbaV0sIGN0X2ludF9leHAkY29uZFtpXSksIGNvbGxhcHNlID0gIiAiKQogICAgdHVwMiA9IHBhc3RlKGMoY3RfaW50X2V4cCRnZW5lX3RhcmdldFtpXSwgY3RfaW50X2V4cCRnZW5lW2ldLCBjdF9pbnRfZXhwJGN0X3RhcmdldFtpXSwgCiAgICAgICAgICAgICAgICAgICBjdF9pbnRfZXhwJGN0W2ldLCBjdF9pbnRfZXhwJGNvbmRbaV0pLCBjb2xsYXBzZSA9ICIgIikKICAgIGlmKCEodHVwMSAlaW4lIHR1cF9saXN0KSAmICEodHVwMiAlaW4lIHR1cF9saXN0KSl7CiAgICAgIHR1cF9saXN0ID0gYyh0dXBfbGlzdCwgdHVwMSwgdHVwMikKICAgICAga2VlcF9yb3cgPSBjKGtlZXBfcm93LCBUKQogICAgfSBlbHNlewogICAgICBrZWVwX3JvdyA9IGMoa2VlcF9yb3csIEYpCiAgICB9CiAgfQogIGN0X2ludF9leHAgPSBjdF9pbnRfZXhwW2tlZXBfcm93LF0KICAKICBjdF9pbnRfZXhwID0gbWVyZ2UoY3RfaW50X2V4cCwgdW5pcXVlKGN0X2dfY29uZF9hbm5bLGMoMSw0KV0pLCBieSA9IDEsIGFsbCA9IFQpCiAgbm4gPSBpZihzaW1wKSAic2ltcCIgZWxzZSAiYWxsIgogIGN0X2ludF9leHBfbFtbbm5dXSA9IGN0X2ludF9leHAKICBjdF9pbnRfZXhwX2xbW25uXV0gPSBjdF9pbnRfZXhwX2xbW25uXV1bY29tcGxldGUuY2FzZXMoY3RfaW50X2V4cF9sW1tubl1dKSxdCiAgCiAgY3RfaW50X2V4cF9sW1tubl1dJGNvbmQgPSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KGN0X2ludF9leHBfbFtbbm5dXSRjb25kLCAiXyIpLCBmdW5jdGlvbih4KSB4WzFdKSkKfQoKc2F2ZVJEUyhjdF9pbnRfZXhwX2wsIGZpbGUgPSAicmVzdWx0cy9jZWxsX2NvbW0vdjMvaW50ZXJhY3RfY2VsbHR5cGVfZXhwX2dyb3VwX2xpc3QuUkRTIikKYGBgCgpWYXJpYWJpbGl0eSBvZiBpbnRlcmFjdGlvbnMKCmBgYHtyfQppbnRlcl9hbm5vdCA9IHJlYWQuY3N2KCJkYXRhL2ludGVyYWN0aW9uX2Fubm90YXRpb24yLmNzdiIsIGhlYWRlciA9IFQpCmludGVyX2Fubm90JGZ1bmN0W2dyZXBsKCJpbW0iLCBpbnRlcl9hbm5vdCRmdW5jdCldID0gImltbXVuZSIKaW50ZXJfYW5ub3QkZnVuY3RbZ3JlcGwoImluZmxhbSIsIGludGVyX2Fubm90JGZ1bmN0KV0gPSAiaW1tdW5lIgpnciA9IGludGVyX2Fubm90JGZ1bmN0Cm5hbWVzKGdyKSA9IGludGVyX2Fubm90JGludGVyCmdyX2xpc3QgPSB0YXBwbHkoaW50ZXJfYW5ub3QkaW50ZXIsIGludGVyX2Fubm90JGZ1bmN0LCBmdW5jdGlvbih4KSB4KQoKaGVyX2FsbGludF9sID0gbGlzdCgpCmZvcihzaW1wIGluIGMoVCwgRikpewogIG5fdXNlID0gbmFtZXMocmVmb3JtX2xpc3QpW2dyZXBsKCJzaW1wIiwgbmFtZXMocmVmb3JtX2xpc3QpKT09c2ltcF0KICAKICBoZV9hbGxpbnQgPSBtZXJnZShyZWZvcm1fbGlzdFtbbl91c2VbMV1dXVssMTo2XSwgcmVmb3JtX2xpc3RbW25fdXNlWzJdXV1bLDE6Nl0sIGJ5ID0gMTozLCBhbGwgPSBUKQogIGhlX2FsbGludCRscjEueFtpcy5uYShoZV9hbGxpbnQkbHIxLngpXSA9IGhlX2FsbGludCRscjEueVtpcy5uYShoZV9hbGxpbnQkbHIxLngpXQogIGhlX2FsbGludCRscjIueFtpcy5uYShoZV9hbGxpbnQkbHIyLngpXSA9IGhlX2FsbGludCRscjIueVtpcy5uYShoZV9hbGxpbnQkbHIyLngpXQogIGhlX2FsbGludCA9IGhlX2FsbGludFssYygxOjYsOSldCiAgaGVyX2FsbGludCA9IG1lcmdlKGhlX2FsbGludCwgcmVmb3JtX2xpc3RbW25fdXNlWzNdXV1bLDE6Nl0sIGJ5ID0gMTozLCBhbGwgPSBUKQogIGhlcl9hbGxpbnQkbHIxLnhbaXMubmEoaGVyX2FsbGludCRscjEueCldID0gaGVyX2FsbGludCRscjFbaXMubmEoaGVyX2FsbGludCRscjEueCldCiAgaGVyX2FsbGludCRscjIueFtpcy5uYShoZXJfYWxsaW50JGxyMi54KV0gPSBoZXJfYWxsaW50JGxyMltpcy5uYShoZXJfYWxsaW50JGxyMi54KV0KICBoZXJfYWxsaW50ID0gaGVyX2FsbGludFssYygxOjcsMTApXQogIGhlcl9hbGxpbnRbaXMubmEoaGVyX2FsbGludCldID0gMAogIGNvbG5hbWVzKGhlcl9hbGxpbnQpWzQ6OF0gPSBjKCJscjEiLCAibHIyIiwgImhlYWx0aHlfZXhwIiwgImVtYm9saXNlZF9leHAiLCAicmVnZW5lcmF0aW5nX2V4cCIpCiAgCiAgIyBjb3VudCBvY2N1cnJlbmNlcyBwZXIgY29uZGl0aW9uLiB0aGlzIHdvcmtzIGJjIHdlJ3JlIGFscmVhZHkgd29ya2luZyB3aXRoIHNpZyBtZWFucwogIGhlcl9hbGxpbnQgPSBtZXJnZShoZXJfYWxsaW50LCBkYXRhLmZyYW1lKHRhYmxlKGhlcl9hbGxpbnQkaWRfY3BfaW50ZXJhY3Rpb25baGVyX2FsbGludCRoZWFsdGh5X2V4cD4wXSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IDEsIGFsbC54ID0gVCkKICBoZXJfYWxsaW50ID0gbWVyZ2UoaGVyX2FsbGludCwgZGF0YS5mcmFtZSh0YWJsZShoZXJfYWxsaW50JGlkX2NwX2ludGVyYWN0aW9uW2hlcl9hbGxpbnQkZW1ib2xpc2VkX2V4cD4wXSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IDEsIGFsbC54ID0gVCkKICBoZXJfYWxsaW50ID0gbWVyZ2UoaGVyX2FsbGludCwgCiAgICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUodGFibGUoaGVyX2FsbGludCRpZF9jcF9pbnRlcmFjdGlvbltoZXJfYWxsaW50JHJlZ2VuZXJhdGluZ19leHA+MF0pKSwgCiAgICAgICAgICAgICAgICAgICAgIGJ5ID0gMSwgYWxsLnggPSBUKQogIGNvbG5hbWVzKGhlcl9hbGxpbnQpWzk6MTFdID0gYygiaGVhbHRoeV9uIiwgImVtYm9saXNlZF9uIiwgInJlZ2VuZXJhdGluZ19uIikKICBoZXJfYWxsaW50W2lzLm5hKGhlcl9hbGxpbnQpXSA9IDAKICBoZXJfYWxsaW50JGN0X3BhaXIgPSBmYWN0b3IocGFzdGUwKGhlcl9hbGxpbnQkY3QxLCAiXyIsIGhlcl9hbGxpbnQkY3QyKSkKICAKICBjb21iX2NvbmQgPSBjb21ibihjb2xuYW1lcyhoZXJfYWxsaW50KVs2OjhdLDIpCiAgY29sbmFtZXMoY29tYl9jb25kKSA9IGMoImhlIiwgImhyIiwgImVyIikKICBmb3IoaSBpbiBjb2xuYW1lcyhjb21iX2NvbmQpKXsKICAgIHBsb3RfZGYgPSBoZXJfYWxsaW50W2hlcl9hbGxpbnRbLGNvbWJfY29uZFsxLGldXT4wIHwgaGVyX2FsbGludFssY29tYl9jb25kWzIsaV1dPjAsMToxMl0KICAgIAogICAgZXhwX2RmMSA9IHJlc2hhcGUyOjpkY2FzdChwbG90X2RmLCBmb3JtdWxhID0gaWRfY3BfaW50ZXJhY3Rpb24gfiBjdF9wYWlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUudmFyID0gY29tYl9jb25kWzEsaV0sIGZpbGwgPSAwKQogICAgcm93bmFtZXMoZXhwX2RmMSkgPSBleHBfZGYxWywxXQogICAgZXhwX2RmMSA9IGV4cF9kZjFbLC0xXT4wCiAgICBleHBfZGYyID0gcmVzaGFwZTI6OmRjYXN0KHBsb3RfZGYsIGZvcm11bGEgPSBpZF9jcF9pbnRlcmFjdGlvbiB+IGN0X3BhaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS52YXIgPSBjb21iX2NvbmRbMixpXSwgZmlsbCA9IDApCiAgICByb3duYW1lcyhleHBfZGYyKSA9IGV4cF9kZjJbLDFdCiAgICBleHBfZGYyID0gZXhwX2RmMlssLTFdPjAKICAgIAogICAgcGxvdF9kZiA9IG1lcmdlKHBsb3RfZGYsIHNhcHBseShyb3duYW1lcyhleHBfZGYxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIGluZm90aGVvOjptdXRpbmZvcm1hdGlvbihleHBfZGYxW3gsXSwgZXhwX2RmMlt4LF0pKSwKICAgICAgICAgICAgICAgICAgICBieS54ID0gMSwgYnkueSA9IDAsIGFsbC54ID0gVCkKICAgIHBsb3RfZGYgPSBtZXJnZShwbG90X2RmLCBzYXBwbHkocm93bmFtZXMoZXhwX2RmMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSBlMTA3MTo6aGFtbWluZy5kaXN0YW5jZShleHBfZGYxW3gsXSwgZXhwX2RmMlt4LF0pKSwKICAgICAgICAgICAgICAgICAgICBieS54ID0gMSwgYnkueSA9IDAsIGFsbC54ID0gVCkKICAgIHBsb3RfZGYgPSBtZXJnZShwbG90X2RmLCBzYXBwbHkocm93bmFtZXMoZXhwX2RmMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSBlMTA3MTo6aGFtbWluZy5kaXN0YW5jZShleHBfZGYxW3gsXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwX2RmMlt4LF0pL3N1bShleHBfZGYxW3gsXSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBfZGYyW3gsXSkpLAogICAgICAgICAgICAgICAgICAgIGJ5LnggPSAxLCBieS55ID0gMCwgYWxsLnggPSBUKQogICAgcGxvdF9kZiA9IG1lcmdlKHBsb3RfZGYsIHNhcHBseShyb3duYW1lcyhleHBfZGYxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHN1bShleHBfZGYxW3gsXSB8IGV4cF9kZjJbeCxdKSksCiAgICAgICAgICAgICAgICAgICAgYnkueCA9IDEsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCiAgICAKICAgIGNvbG5hbWVzKHBsb3RfZGYpWzEzOjE2XSA9IHBhc3RlMChjKCJtdXRJbmZvXyIsICJoYW1tXyIsICJoYW1tTm9ybV8iLCAidG90XyIpLCBpKQogICAgCiAgICBoZXJfYWxsaW50ID0gbWVyZ2UoaGVyX2FsbGludCwgdW5pcXVlKHBsb3RfZGZbLGMoMSwxMzoxNildKSwgYWxsLnggPSBULCBieSA9IDEpCiAgfQogIGhlcl9hbGxpbnQkbXV0SW5mb19lcltpcy5uYShoZXJfYWxsaW50JG11dEluZm9fZXIpXSA9IDEKICBoZXJfYWxsaW50JG11dEluZm9faHJbaXMubmEoaGVyX2FsbGludCRtdXRJbmZvX2hyKV0gPSAxCiAgaGVyX2FsbGludCRtdXRJbmZvX2hlW2lzLm5hKGhlcl9hbGxpbnQkbXV0SW5mb19oZSldID0gMQogIGhlcl9hbGxpbnQkdG90X2hlW2lzLm5hKGhlcl9hbGxpbnQkdG90X2hlKV0gPSAwCiAgaGVyX2FsbGludCR0b3RfaHJbaXMubmEoaGVyX2FsbGludCR0b3RfaHIpXSA9IDAKICBoZXJfYWxsaW50JHRvdF9lcltpcy5uYShoZXJfYWxsaW50JHRvdF9lcildID0gMAogIGhlcl9hbGxpbnQkaGFtbV9lcltpcy5uYShoZXJfYWxsaW50JGhhbW1fZXIpXSA9IDAKICBoZXJfYWxsaW50JGhhbW1faHJbaXMubmEoaGVyX2FsbGludCRoYW1tX2hyKV0gPSAwCiAgaGVyX2FsbGludCRoYW1tX2hlW2lzLm5hKGhlcl9hbGxpbnQkaGFtbV9oZSldID0gMAogIGhlcl9hbGxpbnQkaGFtbU5vcm1fZXJbaXMubmEoaGVyX2FsbGludCRoYW1tTm9ybV9lcildID0gMAogIGhlcl9hbGxpbnQkaGFtbU5vcm1faGVbaXMubmEoaGVyX2FsbGludCRoYW1tTm9ybV9oZSldID0gMAogIGhlcl9hbGxpbnQkaGFtbU5vcm1faHJbaXMubmEoaGVyX2FsbGludCRoYW1tTm9ybV9ocildID0gMAogIAogIGhlcl9hbGxpbnQkZGlmZl9uX2hlID0gaGVyX2FsbGludCRlbWJvbGlzZWRfbi1oZXJfYWxsaW50JGhlYWx0aHlfbgogIGhlcl9hbGxpbnQkZGlmZl9uX2hyID0gaGVyX2FsbGludCRyZWdlbmVyYXRpbmdfbi1oZXJfYWxsaW50JGhlYWx0aHlfbgogIGhlcl9hbGxpbnQkZGlmZl9uX2VyID0gaGVyX2FsbGludCRyZWdlbmVyYXRpbmdfbi1oZXJfYWxsaW50JGVtYm9saXNlZF9uCiAgCiAgIyBwbG90IHRvdCB2cyBtdXR1YWwKICBwbG90X2RmID0gdW5pcXVlKGhlcl9hbGxpbnRbLGMoImlkX2NwX2ludGVyYWN0aW9uIiwgcGFzdGUwKGMoIm11dEluZm9fIiwgInRvdF8iLCAiZGlmZl9uXyIpLCBpKSldKQogIHBsdCA9IGdncGxvdChwbG90X2RmLCBhZXMoeCA9IHRvdF9lciwgeSA9IG11dEluZm9fZXIqKGRpZmZfbl9lci9hYnMoZGlmZl9uX2VyKSkpKSsKICAgIGdlb21fYmluMmQoKSsKICAgIHNjYWxlX3hfbG9nMTAoKSsKICAgIHRoZW1lX2J3KCkrCiAgICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQogIHByaW50KHBsdCkKICAKICBubiA9IGlmKHNpbXApICJzaW1wIiBlbHNlICJhbGwiCiAgaGVyX2FsbGludF9sW1tubl1dID0gaGVyX2FsbGludAp9CgpzYXZlUkRTKGhlcl9hbGxpbnRfbCwgZmlsZSA9ICIuL3Jlc3VsdHMvY2VsbF9jb21tL3YzL2ludGVyYWN0aW9uc19tdXRJbmZvX2NvbmRDb21wLlJEUyIpCmBgYAoKR1NFQSBvZiBpbnRlcmFjdGlvbnMgdXNpbmcgbXV0dWFsIGluZm9ybWF0aW9uCgpgYGB7cn0KZm9yKG4gaW4gbmFtZXMoaGVyX2FsbGludF9sKSl7CiAgaGVyX2FsbGludCA9IGhlcl9hbGxpbnRfbFtbbl1dCiAgCiAgY29tYl9jb25kID0gY29tYm4oY29sbmFtZXMoaGVyX2FsbGludClbNjo4XSwyKQogIGNvbG5hbWVzKGNvbWJfY29uZCkgPSBjKCJoZSIsICJociIsICJlciIpCiAgZ3NlYV9saXN0ID0gbGlzdCgpCiAgZm9yKGkgaW4gY29sbmFtZXMoY29tYl9jb25kKSl7CiAgICB2YWxzID0gdW5pcXVlKGhlcl9hbGxpbnRbLGMoImlkX2NwX2ludGVyYWN0aW9uIiwgcGFzdGUwKCJtdXRJbmZvXyIsIGkpKV0pCiAgICB2YWxzMiA9IHZhbHNbLHBhc3RlMCgibXV0SW5mb18iLCBpKV0gCiAgICBuYW1lcyh2YWxzMikgPSB2YWxzJGlkX2NwX2ludGVyYWN0aW9uCiAgICAKICAgIHNldC5zZWVkKDEpCiAgICBnc2VhX2xpc3RbW2ldXSA9IGxpZ2VyOjpidWxrLmdzZWEoMS12YWxzMiwgc2V0Lmxpc3QgPSBncl9saXN0LCBuLnJhbmQgPSAxMDAwMDAwKQogICAgZ3NlYV9saXN0W1tpXV0kZ3JvdXAgPSByb3duYW1lcyhnc2VhX2xpc3RbW2ldXSkKICAgIGdzZWFfbGlzdFtbaV1dJGNvbXAgPSBpCiAgfQogIAogIGxpZ2VyOjpnc2VhKDEtdmFsczIsIGdlbmVzZXQgPSBncl9saXN0JGltbXVuZSwgcGxvdCA9IFQpCiAgCiAgIyBwbG90IEdTRUEgZW5yaWNobWVudAogIHBsb3RfZGYgPSBSZWR1Y2UocmJpbmQsIGdzZWFfbGlzdCkKICBwbG90X2RmJHEudmFsID0gLWxvZzEwKHBsb3RfZGYkcS52YWwrMC4wMDAwMDA1KSoocGxvdF9kZiRzc2NvcmUvYWJzKHBsb3RfZGYkc3Njb3JlKSkKICBwbG90X2RmJGNvbXAgPSBmYWN0b3IocGxvdF9kZiRjb21wLCBsZXZlbHMgPSByZXYoYygiaGUiLCJociIsImVyIikpKQogIG0gPSB0YXBwbHkocGxvdF9kZiRxLnZhbCwgcGxvdF9kZiRncm91cCxtZWFuKQogIHBsb3RfZGYkZ3JvdXAgPSBmYWN0b3IocGxvdF9kZiRncm91cCwgbGV2ZWxzID0gbmFtZXMobSlbb3JkZXIobSwgZGVjcmVhc2luZyA9IEYpXSkKICBwbHQgPSBnZ3Bsb3QocGxvdF9kZiwgYWVzKHggPSBxLnZhbCwgeSA9IGdyb3VwLCBmaWxsID0gY29tcCkpKwogICAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMobG9nMTAoMC4wNSksIC1sb2cxMCgwLjA1KSksIGxpbmV0eXBlID0gImRhc2hlZCIpKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygwKSkrCiAgICBsYWJzKHggPSAicS12YWx1ZSB4IHNjb3JlIHNpZ25hbCIsIHkgPSAiaW50ZXJhY3Rpb24gdHlwZSIsIGZpbGwgPSAiY29tcGFyaXNvbiIpKwogICAgdGhlbWVfYncoKSsKICAgIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gNyksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIsIHNpemUgPSA3LjUpLAogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLDEpLAogICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKC0wLjA1LDEuMDUpLAogICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA3LjUpLAogICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpLAogICAgICAgICAgbGVnZW5kLm1hcmdpbiA9IG1hcmdpbigwLDAsMCwwKSwKICAgICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC4zNSwgImNtIikpCiAgcHJpbnQocGx0KQogIAogIHNhdmVSRFMoZ3NlYV9saXN0LCBmaWxlID0gcGFzdGUwKCIuL3Jlc3VsdHMvY2VsbF9jb21tL3YzLyIsIG4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdzZWFfaW50ZXJhY3Rpb25zX211dEluZm9fY29uZENvbXAuUkRTIikpCn0KYGBgCgpTYXZlIG11dHVhbCBpbmZvcm1hdGlvbiBmb3IgTFIgYW5kIGNlbGwgdHlwZXMKCmBgYHtyfQpmb3IobiBpbiBuYW1lcyhoZXJfYWxsaW50X2wpKXsKICBoZXJfYWxsaW50ID0gaGVyX2FsbGludF9sW1tuXV0KICAjIHNlbGVjdCBnZW5lcyBmcm9tIGxvd2VzdCBtdXR1YWwgKHRhYmxlIC0gZ2V0IG1vc3QgY29tbW9uKQogICMgKwogICMgbWVhbiBtdXR1YWwgcGVyIGNlbGwgdHlwZSAobG93ZXN0ID0gbW9yZSBjaGFuZ2UpCiAgIyMgcGxvdCBpbnRlcmFjdGlvbnMgYmFzZWQgb24gdGhvc2UgZ2VuZXMgKHNvbWUgYXJlIGludm9sdmVkIGluIG1vcmUgdGhhbiBvbmUpCiAgIyMgaGVhdG1hcCAtIHJvd3MgY3Q7IGNvbHVtbnMgZ2VuZXM7IGdhcHMgYmV0d2VlbiBpbnRlcmFjdDsgMSBoZWF0bWFwL2NvbmQsIHNhbWUgY3Qgb3JkZXJpbmcKICBzdWJfZGYxID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaGU+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImN0MSIsIm11dEluZm9faGUiKV0pCiAgc3ViX2RmMiA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2hlPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJjdDIiLCJtdXRJbmZvX2hlIildKQogIGN0X211dF9oZSA9IHRhcHBseShjKHN1Yl9kZjEkbXV0SW5mb19oZSwgc3ViX2RmMiRtdXRJbmZvX2hlKSwgCiAgICAgICAgICAgICAgICAgICAgIGMoc3ViX2RmMSRjdDEsIHN1Yl9kZjIkY3QyKSwgbWVhbikKICBzdWJfZGYxID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaHI+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImN0MSIsIm11dEluZm9faHIiKV0pCiAgc3ViX2RmMiA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2hyPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJjdDIiLCJtdXRJbmZvX2hyIildKQogIGN0X211dF9ociA9IHRhcHBseShjKHN1Yl9kZjEkbXV0SW5mb19ociwgc3ViX2RmMiRtdXRJbmZvX2hyKSwgCiAgICAgICAgICAgICAgICAgICAgIGMoc3ViX2RmMSRjdDEsIHN1Yl9kZjIkY3QyKSwgbWVhbikKICBzdWJfZGYxID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfZXI+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImN0MSIsIm11dEluZm9fZXIiKV0pCiAgc3ViX2RmMiA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2VyPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJjdDIiLCJtdXRJbmZvX2VyIildKQogIGN0X211dF9lciA9IHRhcHBseShjKHN1Yl9kZjEkbXV0SW5mb19lciwgc3ViX2RmMiRtdXRJbmZvX2VyKSwgCiAgICAgICAgICAgICAgICAgICAgIGMoc3ViX2RmMSRjdDEsIHN1Yl9kZjIkY3QyKSwgbWVhbikKICBjdF9tdXRfZGYgPSBjYmluZChjdF9tdXRfaGUsY3RfbXV0X2hyLGN0X211dF9lcikKICByb3duYW1lcyhjdF9tdXRfZGYpID0gbmFtZXMoY3RfbXV0X2hlKQogIGNvbG5hbWVzKGN0X211dF9kZikgPSBjKCJoZSIsICJociIsICJlciIpCiAgc2F2ZVJEUyhjdF9tdXRfZGYsIGZpbGUgPSAiLi9yZXN1bHRzL2NlbGxfY29tbS92My9jdF9zZWxlY3RfbXV0SW5mb19jb25kQ29tcC5SRFMiKQogIAogIHN1Yl9kZjEgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9oZT4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwibHIxIiwibXV0SW5mb19oZSIpXSkKICBzdWJfZGYyID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaGU+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImxyMiIsIm11dEluZm9faGUiKV0pCiAgbHJfbXV0X2hlID0gdGFwcGx5KGMoc3ViX2RmMSRtdXRJbmZvX2hlLCBzdWJfZGYyJG11dEluZm9faGUpLCAKICAgICAgICAgICAgICAgICAgICAgYyhzdWJfZGYxJGxyMSwgc3ViX2RmMiRscjIpLCBtZWFuKQogIHN1Yl9kZjEgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9ocj4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwibHIxIiwibXV0SW5mb19ociIpXSkKICBzdWJfZGYyID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaHI+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImxyMiIsIm11dEluZm9faHIiKV0pCiAgbHJfbXV0X2hyID0gdGFwcGx5KGMoc3ViX2RmMSRtdXRJbmZvX2hyLCBzdWJfZGYyJG11dEluZm9faHIpLCAKICAgICAgICAgICAgICAgICAgICAgYyhzdWJfZGYxJGxyMSwgc3ViX2RmMiRscjIpLCBtZWFuKQogIHN1Yl9kZjEgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9lcj4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwibHIxIiwibXV0SW5mb19lciIpXSkKICBzdWJfZGYyID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfZXI+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImxyMiIsIm11dEluZm9fZXIiKV0pCiAgbHJfbXV0X2VyID0gdGFwcGx5KGMoc3ViX2RmMSRtdXRJbmZvX2VyLCBzdWJfZGYyJG11dEluZm9fZXIpLCAKICAgICAgICAgICAgICAgICAgICAgYyhzdWJfZGYxJGxyMSwgc3ViX2RmMiRscjIpLCBtZWFuKQogIAogIGxyX2NudF9oZSA9IHRhYmxlKGMoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9oZT4wICYgaGVyX2FsbGludCRtdXRJbmZvX2hlPD0wLjA1LCJscjEiXSwKICAgICAgICAgICAgICAgICAgICAgIGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaGU+MCAmIGhlcl9hbGxpbnQkbXV0SW5mb19oZTw9MC4wNSwibHIyIl0pKQogIGxyX2hlID0gbWVyZ2UobHJfY250X2hlLCBscl9tdXRfaGUsIGJ5LnggPSAxLCBieS55ID0gMCkKICBscl9jbnRfaHIgPSB0YWJsZShjKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaHI+MCAmIGhlcl9hbGxpbnQkbXV0SW5mb19ocjw9MC4wNSwibHIxIl0sCiAgICAgICAgICAgICAgICAgICAgICBoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2hyPjAgJiBoZXJfYWxsaW50JG11dEluZm9faHI8PTAuMDUsImxyMiJdKSkKICBscl9ociA9IG1lcmdlKGxyX2NudF9ociwgbHJfbXV0X2hyLCBieS54ID0gMSwgYnkueSA9IDApCiAgbHJfY250X2VyID0gdGFibGUoYyhoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2VyPjAgJiBoZXJfYWxsaW50JG11dEluZm9fZXI8PTAuMDUsImxyMSJdLAogICAgICAgICAgICAgICAgICAgICAgaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9lcj4wICYgaGVyX2FsbGludCRtdXRJbmZvX2VyPD0wLjA1LCJscjIiXSkpCiAgbHJfZXIgPSBtZXJnZShscl9jbnRfZXIsIGxyX211dF9lciwgYnkueCA9IDEsIGJ5LnkgPSAwKQogIAogICMgRUNNIC0gd2hpY2ggcHJvdGVpbnMvY29sbGFnZW5zPzsgbWVudGlvbiBUR0ZCCiAgIyBkZXYgLSB3aGljaCBsaWdhbmRzL3JlY2VwdG9ycwogIGxyX2FsbCA9IHJiaW5kKGxyX2hlLCBscl9ociwgbHJfZXIpCiAgbHJfYWxsJGNvbmQgPSBjKHJlcCgiaGUiLCBucm93KGxyX2hlKSksIHJlcCgiaHIiLCBucm93KGxyX2hyKSkscmVwKCJlciIsIG5yb3cobHJfZXIpKSkKICBjb2xuYW1lcyhscl9hbGwpID0gYygibHIiLCAibl9tdXQuMDUiLCAibWVhbl9tdXRJbmZvIiwgImNvbmQiKQogIHNhdmVSRFMobHJfYWxsLCBmaWxlID0gcGFzdGUwKCIuL3Jlc3VsdHMvY2VsbF9jb21tL3YzLyIsIG4sICJMUl9zZWxlY3RfbXV0SW5mb19jb25kQ29tcC5SRFMiKSkKfQpgYGAKClBsb3QgaW50ZXJhY3Rpb25zCgpgYGB7cn0KZm9yKG4gaW4gbmFtZXMoY3RfaW50X2V4cF9sKSl7CiAgY3RfaW50X2V4cCA9IGN0X2ludF9leHBfbFtbbl1dCiAgciA9IGlmKG49PSJhbGwiKSAxOjMgZWxzZSA0OjYKICAKICAjIGludGVyYWN0aW9ucwogIGludGRmID0gdW5pcXVlKFJlZHVjZShyYmluZCwgcmVmb3JtX2xpc3Rbcl0pWyxjKDEsNCw1KV0pCiAgaW50ZGYkaW50cGFpciA9IHBhc3RlMChpbnRkZiRscjEsICIgLSAiLCBpbnRkZiRscjIpCiAgCiAgY3RfaW50X2V4cF9maWxlID0gdW5pcXVlKGN0X2ludF9leHBbLGMoMSw0OjUsNzoxMCldKQogIGN0X2ludF9leHBfZmlsZSRjdHBhaXIgPSBwYXN0ZTAoY3RfaW50X2V4cF9maWxlJGN0LCAiIC0gIiwgY3RfaW50X2V4cF9maWxlJGN0X3RhcmdldCkKICAKICBwbG90X2RmX2ludCA9IHJlc2hhcGUyOjptZWx0KGN0X2ludF9leHBfZmlsZVssYygxLDgsNDo3KV0pCiAgcGxvdF9kZl9pbnQkdmFyaWFibGUgPSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KGFzLmNoYXJhY3RlcihwbG90X2RmX2ludCR2YXJpYWJsZSksICJfIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB4W1sxXV1bMV0pKQogIHBsb3RfZGZfaW50JHZhcmlhYmxlID0gZmFjdG9yKHBsb3RfZGZfaW50JHZhcmlhYmxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSByZXYoYygiaGVhbHRoeSIsICJlbWJvbGlzZWQiLCAicmVnZW5lcmF0aW5nIikpKQogIAogIHN1Yl9wbG90X2RmX2ludCA9IHBsb3RfZGZfaW50W2dyZXBsKCJTdGVsbGF0ZSIsIHBsb3RfZGZfaW50JGN0cGFpcikgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIkt1cGZmZXIiLCBwbG90X2RmX2ludCRjdHBhaXIpIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJMU0VDIiwgcGxvdF9kZl9pbnQkY3RwYWlyKSxdCiAgCiAgc3ViX3Bsb3RfZGZfaW50ID0gbWVyZ2Uoc3ViX3Bsb3RfZGZfaW50LCBpbnRkZlssYygxLDQpXSwgYnkgPSAxLCBhbGwueCA9IFQpCiAgCiAgZ2cgPSAiRUNNIgogIHBsdCA9IGdncGxvdChzdWJfcGxvdF9kZl9pbnRbc3ViX3Bsb3RfZGZfaW50JGRlc2NyaXB0aW9uPT1nZyxdLCAKICAgICAgICAgYWVzKHggPSBjdHBhaXIsIHkgPSB2YXJpYWJsZSwgY29sb3VyID0gdmFsdWUsIHNpemUgPSB2YWx1ZSkpKwogICAgZmFjZXRfZ3JpZChpbnRwYWlyfi4pKwogICAgZ3VpZGVzKHNpemUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiZXhwIiwgcmV2ZXJzZSA9IFQpLCAKICAgICAgICAgICBjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiZXhwIiwgcmV2ZXJzZSA9IFQpKSsKICAgIGdlb21fcG9pbnQoKSsKICAgIGxhYnModGl0bGUgPSBnZykrCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMSksCiAgICAgICAgICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBzaXplID0gOCkpCiAgcHJpbnQocGx0KQp9CmBgYAoKSW1wb3J0YW50IHRhYmxlcwoKYGBge3J9CmZvcihuIGluIG5hbWVzKGludGVyX2RmKSl7CiAgd3JpdGUuY3N2KGludGVyX2RmW1tuXV0sIGNvbC5uYW1lcyA9IFQsIHJvdy5uYW1lcyA9IEYsIHF1b3RlID0gRiwKICAgICAgICAgICAgZmlsZSA9IHBhc3RlMCgicmVzdWx0cy9jZWxsX2NvbW0vdjMvdGFibGVzL0ludGVyYWN0XyIsIG4sICJfY2VsbHR5cGVzX2NvbmQuY3N2IikpCn0KZm9yKG4gaW4gbmFtZXMoY3RfaW50X2V4cF9sKSl7CiAgY3RfaW50X2V4cCA9IGN0X2ludF9leHBfbFtbbl1dCiAgd3JpdGUuY3N2KGN0X2ludF9leHAsIGNvbC5uYW1lcyA9IFQsIHJvdy5uYW1lcyA9IEYsIHF1b3RlID0gRiwgCiAgICAgICAgICAgIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvY2VsbF9jb21tL3YzL3RhYmxlcy8iLCBuLCAiaW50ZXJhY3RfY2VsbHR5cGVfZXhwX2dyb3VwLmNzdiIpKQp9CmBgYAoKCiMjIENlbGwtY2VsbCBjb21tdW5pY2F0aW9uIG5ldHdvcmtzCkxvYWQgZGF0YSB0byBtYWtlIGNlbGwgY29tbSBuZXR3b3JrcyAoTk9UIFVTRUQgSEVSRSkKCmBgYHtyfQpyZWRvbmVfbWV0YSA9IGxpc3QoKQpmb3IoY2MgaW4gdW5pcXVlKGFsbGNlbGxzX2NzcyRDb25kaXRpb24pKXsKICByZWRvbmVfbWV0YVtbY2NdXSA9IHJlYWQudGFibGUocGFzdGUwKCJyZXN1bHRzL2NlbGxfY29tbS9DZWxsUGhvbmVEQl9WMy8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNjLCAiLyIsIGNjLCAiX21ldGFfbmFtZXMudHh0IiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxKQp9CnJlZG9uZV9tZXRhX2FsbCA9IFJlZHVjZShyYmluZCwgcmVkb25lX21ldGEpCgphbGxjZWxsc19yZWRvbmUgPSBBZGRNZXRhRGF0YShhbGxjZWxsc19jc3MsIHJlZG9uZV9tZXRhX2FsbCkKYWxsY2VsbHNfcmVkb25lID0gYWxsY2VsbHNfcmVkb25lWywhaXMubmEoYWxsY2VsbHNfcmVkb25lJGNlbGxfdHlwZSldCmBgYAoKRnVuY3Rpb25zIHVzZWQgdG8gbWFrZSBjZWxsIGNvbW0gbmV0d29ya3MKCmBgYHtyfQptYWtlTWVkaWFuID0gZnVuY3Rpb24ocG9pbnRfZGYsIGVkZ2VfZGYsIGNsID0gYygiY3QyIiwgIm1hal9nMSIsICJtYWpfZzIiKSl7CiAgbWVhbl9tYWpvciA9IGRhdGEuZnJhbWUoIlgxIiA9IHRhcHBseShwb2ludF9kZiRYMSwgcG9pbnRfZGZbLGNsWzFdXSwgbWVkaWFuKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAiWDIiID0gdGFwcGx5KHBvaW50X2RmJFgyLCBwb2ludF9kZlssY2xbMV1dLCBtZWRpYW4pKQogIG1lYW5fbWFqb3JbLGNsWzFdXSA9IHJvd25hbWVzKG1lYW5fbWFqb3IpCiAgCiAgZWRnZV9kZlssY2xbMl1dID0gZmFjdG9yKGVkZ2VfZGZbLGNsWzJdXSwgbGV2ZWxzID0gdW5pcXVlKGMoZWRnZV9kZlssY2xbMl1dLCBlZGdlX2RmWyxjbFszXV0pKSkKICBlZGdlX2RmWyxjbFszXV0gPSBmYWN0b3IoZWRnZV9kZlssY2xbM11dLCBsZXZlbHMgPSB1bmlxdWUoYyhhcy5jaGFyYWN0ZXIoZWRnZV9kZlssY2xbMl1dKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZGdlX2RmWyxjbFszXV0pKSkKICAKICBtYWpfbWF0ID0gdGFibGUoZWRnZV9kZlssY2xbMl1dLCBlZGdlX2RmWyxjbFszXV0pCiAgZGlhZyhtYWpfbWF0KSA9IDAKICBlZGdlX21ham9yID0gZGF0YS5mcmFtZShtYWpfbWF0ICsgdChtYWpfbWF0KSkKICBlZGdlX21ham9yID0gbWVyZ2UoZWRnZV9tYWpvciwgbWVhbl9tYWpvciwgYnkueCA9IDEsIGJ5LnkgPSAzKQogIGVkZ2VfbWFqb3IgPSBtZXJnZShlZGdlX21ham9yLCBtZWFuX21ham9yLCBieS54ID0gMiwgYnkueSA9IDMpCiAgCiAgY2xjb21iID0gY29tYm4odW5pcXVlKGMoYXMuY2hhcmFjdGVyKGVkZ2VfbWFqb3IkVmFyMSksIGFzLmNoYXJhY3RlcihlZGdlX21ham9yJFZhcjIpKSksIDIpCiAga2VlcCA9IGMoKQogIGZvcihqIGluIDE6bmNvbChjbGNvbWIpKXsKICAgIGtlZXAgPSBjKGtlZXAsIHdoaWNoKGVkZ2VfbWFqb3IkVmFyMj09Y2xjb21iWzEsal0gJiBlZGdlX21ham9yJFZhcjE9PWNsY29tYlsyLGpdKSkKICB9CiAgZWRnZV9tYWpvciA9IGVkZ2VfbWFqb3Jba2VlcCxdCiAgCiAgcmV0dXJuKGxpc3QobWVhbl9tYWpvciA9IG1lYW5fbWFqb3IsIGVkZ2VfbWFqb3IgPSBlZGdlX21ham9yKSkKfQoKbWFrZU1lZGlhbkNvbmQgPSBmdW5jdGlvbihwb2ludF9kZiwgZWRnZV9kZiwgY2wgPSBjKCJjdCIsICJjdF9nMSIsICJjdF9nMiIpLCBlZGdlX2J5ID0gImNvbmRpdGlvbiIpewogIG1lYW5fbWFqb3IgPSBkYXRhLmZyYW1lKCJYMSIgPSB0YXBwbHkocG9pbnRfZGYkWDEsIHBvaW50X2RmWyxjbFsxXV0sIG1lZGlhbiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIlgyIiA9IHRhcHBseShwb2ludF9kZiRYMiwgcG9pbnRfZGZbLGNsWzFdXSwgbWVkaWFuKSkKICBtZWFuX21ham9yWyxjbFsxXV0gPSByb3duYW1lcyhtZWFuX21ham9yKQogIAogIGVkZ2VfZGZbLGNsWzJdXSA9IGZhY3RvcihlZGdlX2RmWyxjbFsyXV0sIGxldmVscyA9IHVuaXF1ZShjKGVkZ2VfZGZbLGNsWzJdXSwgZWRnZV9kZlssY2xbM11dKSkpCiAgZWRnZV9kZlssY2xbM11dID0gZmFjdG9yKGVkZ2VfZGZbLGNsWzNdXSwgbGV2ZWxzID0gdW5pcXVlKGMoYXMuY2hhcmFjdGVyKGVkZ2VfZGZbLGNsWzJdXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWRnZV9kZlssY2xbM11dKSkpCiAgCiAgZWRnZV9sID0gbGlzdCgpCiAgZm9yKGkgaW4gdW5pcXVlKGVkZ2VfZGZbLGVkZ2VfYnldKSl7CiAgICBzdWJfZWRnZV9kZiA9IGVkZ2VfZGZbZWRnZV9kZlssZWRnZV9ieV09PWksXQogICAgbWFqX21hdCA9IHRhYmxlKHN1Yl9lZGdlX2RmWyxjbFsyXV0sIHN1Yl9lZGdlX2RmWyxjbFszXV0pCiAgICBkaWFnKG1hal9tYXQpID0gMAogICAgZWRnZV9tYWpvciA9IGRhdGEuZnJhbWUobWFqX21hdCArIHQobWFqX21hdCkpCiAgICBlZGdlX21ham9yID0gbWVyZ2UoZWRnZV9tYWpvciwgbWVhbl9tYWpvciwgYnkueCA9IDEsIGJ5LnkgPSAzKQogICAgZWRnZV9tYWpvciA9IG1lcmdlKGVkZ2VfbWFqb3IsIG1lYW5fbWFqb3IsIGJ5LnggPSAyLCBieS55ID0gMykKICAgIAogICAgIyByZW1vdmUgcmVwZWF0ZWQKICAgIGNsY29tYiA9IGNvbWJuKHVuaXF1ZShjKGFzLmNoYXJhY3RlcihlZGdlX21ham9yJFZhcjEpLCBhcy5jaGFyYWN0ZXIoZWRnZV9tYWpvciRWYXIyKSkpLCAyKQogICAga2VlcCA9IGMoKQogICAgZm9yKGogaW4gMTpuY29sKGNsY29tYikpewogICAgICBrZWVwID0gYyhrZWVwLCB3aGljaChlZGdlX21ham9yJFZhcjI9PWNsY29tYlsxLGpdICYgZWRnZV9tYWpvciRWYXIxPT1jbGNvbWJbMixqXSkpCiAgICB9CiAgICAKICAgIGVkZ2VfbFtbaV1dID0gZWRnZV9tYWpvcltrZWVwLF0KICB9CiAgZWRnZV9tYWpvciA9IFJlZHVjZShyYmluZCwgZWRnZV9sKQogIGVkZ2VfbWFqb3JbLGVkZ2VfYnldID0gdW5saXN0KGxhcHBseShuYW1lcyhlZGdlX2wpLCBmdW5jdGlvbih4KSByZXAoeCwgbnJvdyhlZGdlX2xbW3hdXSkpKSkKICBlZGdlX21ham9yID0gZWRnZV9tYWpvcltlZGdlX21ham9yJFZhcjIhPWVkZ2VfbWFqb3IkVmFyMSxdCiAgCiAgcmV0dXJuKGxpc3QobWVhbl9tYWpvciA9IG1lYW5fbWFqb3IsIGVkZ2VfbWFqb3IgPSBlZGdlX21ham9yKSkKfQpgYGAKClByZXBhcmUgZGF0YQoKYGBge3IsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD00LjZ9CmludGVyX2RmID0gcmVhZFJEUyhmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3YzL2NvbmRfZGlmZl9pbnRlcmFjdF9ERS5SRFMiKQoKIyBpbnRlcmFjdGlvbnMgdW5pcXVlIHRvIGVhY2ggY29uZGl0aW9uCnVuaXF1ZV9pbnRlcnMgPSBjKHNldGRpZmYoaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgYyhpbnRlcl9kZiRlbWJvbGlzZWQkaWRfY3BfaW50ZXJhY3Rpb24sIGludGVyX2RmJHJlZ2VuZXJhdGluZyRpZF9jcF9pbnRlcmFjdGlvbikpLAogICAgICAgICAgICAgICAgICBzZXRkaWZmKGludGVyX2RmJGVtYm9saXNlZCRpZF9jcF9pbnRlcmFjdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgYyhpbnRlcl9kZiRoZWFsdGh5JGlkX2NwX2ludGVyYWN0aW9uLCBpbnRlcl9kZiRyZWdlbmVyYXRpbmckaWRfY3BfaW50ZXJhY3Rpb24pKSwKICAgICAgICAgICAgICAgICAgc2V0ZGlmZihpbnRlcl9kZiRyZWdlbmVyYXRpbmckaWRfY3BfaW50ZXJhY3Rpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgIGMoaW50ZXJfZGYkZW1ib2xpc2VkJGlkX2NwX2ludGVyYWN0aW9uLCBpbnRlcl9kZiRoZWFsdGh5JGlkX2NwX2ludGVyYWN0aW9uKSkpCgojIGludGVyYWN0aW9ucyB1bmlxdWUgdG8gaGVhbHRoeSBvciB0byBlbWIvcmVnZW4KY29tcGhfaW50ZXJzID0gYyhzZXRkaWZmKGludGVyX2RmJGhlYWx0aHkkaWRfY3BfaW50ZXJhY3Rpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgIGMoaW50ZXJfZGYkZW1ib2xpc2VkJGlkX2NwX2ludGVyYWN0aW9uLCBpbnRlcl9kZiRyZWdlbmVyYXRpbmckaWRfY3BfaW50ZXJhY3Rpb24pKSwKICAgICAgICAgICAgICAgICBzZXRkaWZmKGludGVyX2RmJGVtYm9saXNlZCRpZF9jcF9pbnRlcmFjdGlvbiwgaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbiksCiAgICAgICAgICAgICAgICAgc2V0ZGlmZihpbnRlcl9kZiRyZWdlbmVyYXRpbmckaWRfY3BfaW50ZXJhY3Rpb24sIGludGVyX2RmJGhlYWx0aHkkaWRfY3BfaW50ZXJhY3Rpb24pKQoKIyBwcmVwYXJlIGdlbmUgcGFpcnMgcGVyIGNvbmRpdGlvbgpnZW5lX3BhaXJzX2NvbmQgPSByYmluZCh1bmlxdWUoaW50ZXJfZGYkaGVhbHRoeVssYygiaWRfY3BfaW50ZXJhY3Rpb24iLCAiZ24xIiwgImduMiIpXSksCiAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShpbnRlcl9kZiRlbWJvbGlzZWRbLGMoImlkX2NwX2ludGVyYWN0aW9uIiwgImduMSIsICJnbjIiKV0pLAogICAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoaW50ZXJfZGYkcmVnZW5lcmF0aW5nWyxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsICJnbjEiLCAiZ24yIildKSkKZ2VuZV9wYWlyc19jb25kJGNvbmRpdGlvbiA9IGMocmVwKCJoZWFsdGh5IiwgbnJvdyh1bmlxdWUoaW50ZXJfZGYkaGVhbHRoeVssYygiaWRfY3BfaW50ZXJhY3Rpb24iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ24xIiwgImduMiIpXSkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJlbWJvbGlzZWQiLCBucm93KHVuaXF1ZShpbnRlcl9kZiRlbWJvbGlzZWRbLGMoImlkX2NwX2ludGVyYWN0aW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnbjEiLCAiZ24yIildKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoInJlZ2VuZXJhdGluZyIsIG5yb3codW5pcXVlKGludGVyX2RmJHJlZ2VuZXJhdGluZ1ssYygiaWRfY3BfaW50ZXJhY3Rpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ24xIiwgImduMiIpXSkpKSkKCiMgbGlzdCBhbGwgTFIgZ2VuZXMKYWxsX2xyX2dlbmVzID0gdW5pcXVlKGMoYXMuY2hhcmFjdGVyKGludGVyX2RmJGhlYWx0aHkkZ24xKSwgYXMuY2hhcmFjdGVyKGludGVyX2RmJGhlYWx0aHkkZ24yKSwKICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGludGVyX2RmJGVtYm9saXNlZCRnbjEpLCBhcy5jaGFyYWN0ZXIoaW50ZXJfZGYkZW1ib2xpc2VkJGduMiksCiAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihpbnRlcl9kZiRyZWdlbmVyYXRpbmckZ24xKSwgYXMuY2hhcmFjdGVyKGludGVyX2RmJHJlZ2VuZXJhdGluZyRnbjIpKSkKYWxsX2xyX2dlbmVzID0gYWxsX2xyX2dlbmVzW2FsbF9scl9nZW5lcyAlaW4lIHJvd25hbWVzKHN1Yl9hbGxjZWxsc19jc3NAYXNzYXlzJFNDVEBkYXRhKV0KCiMgY2FsY3VsYXRlIG1lYW4gcGVyIGNlbGwgdHlwZSBhbmQgY29uZGl0aW9uIGZvciBlYWNoIExSIGdlbmUKbWVhbl9leHBfY29uZF9sciA9IGFwcGx5KHN1Yl9hbGxjZWxsc19jc3NAYXNzYXlzJFNDVEBkYXRhW2FsbF9scl9nZW5lcyxdLCAxLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHRhcHBseSh4LCBwYXN0ZTAoc3ViX2FsbGNlbGxzX2NzcyRzdWJwb3BzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIl8iLCBzdWJfYWxsY2VsbHNfY3NzJENvbmRpdGlvbiksIG1lYW4pKQoKIyBkZXRlcm1pbmUgdGhlIGNlbGwgdHlwZSBhbmQgY29uZGl0aW9uIHdpdGggdGhlIGhpZ2hlc3QgZXhwcmVzc2lvbgptYXhfY29uZF9jdCA9IHJvd25hbWVzKG1lYW5fZXhwX2NvbmRfbHIpW2FwcGx5KG1lYW5fZXhwX2NvbmRfbHIsIDIsIHdoaWNoLm1heCldCm5hbWVzKG1heF9jb25kX2N0KSA9IGNvbG5hbWVzKG1lYW5fZXhwX2NvbmRfbHIpCm1heF9jb25kX2N0X2N0ID0gdW5saXN0KGxhcHBseShzdHJzcGxpdChtYXhfY29uZF9jdCwgIl8iKSwgZnVuY3Rpb24oeCkgeFsxXSkpCm1heF9jb25kX2N0X2NvbmQgPSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KG1heF9jb25kX2N0LCAiXyIpLCBmdW5jdGlvbih4KSB4W2xlbmd0aCh4KV0pKQoKIyBjb3JyZWxhdGlvbiBvZiBtZWFuIGV4cHJlc3Npb24KY29yX2NvbmRfbHIgPSBjb3IobWVhbl9leHBfY29uZF9sciwgbWV0aG9kID0gInNwIikKCiMgZmlsdGVyIGNvcnJlbGF0aW9uIHdpdGggaXRzZWxmLCBrZWVwIG9ubHkgZ2VuZXMgd2l0aCBjb3I+PTAuMwpkaWFnKGNvcl9jb25kX2xyKSA9IDAKYWRqX2NvbmRfbWF0ID0gY29yX2NvbmRfbHI+PTAuMwpgYGAKClBsb3QgbGlnYW5kcyBhbmQgcmVjZXB0b3JzIHdpdGggTURTCgpgYGB7cn0KIyBidWlsZCBncmFwaCwgcHJvamVjdCB3aXRoIE1EUwpuZXR3b3JrX2NvbmQgPSBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoYWRqX2NvbmRfbWF0LCB3ZWlnaHRlZD1ULCBtb2RlPSJ1bmRpcmVjdGVkIiwgZGlhZz1GKQpsX2NvbmQgPSBpZ3JhcGg6OmxheW91dF93aXRoX21kcyhuZXR3b3JrX2NvbmQpCmxfY29uZCA9IGRhdGEuZnJhbWUobF9jb25kKQpsX2NvbmQkZ2VuZSA9IGNvbG5hbWVzKGFkal9jb25kX21hdCkKcm93bmFtZXMobF9jb25kKSA9IGNvbG5hbWVzKGFkal9jb25kX21hdCkKCiMgZGVmaW5lIGFsbCBlZGdlcywgYmFzZWQgb24gQ2VsbFBob25lREIgcGFpcmluZ3MKdG1wX2RmID0gbWVyZ2UoZ2VuZV9wYWlyc19jb25kLCBsX2NvbmQsIGJ5LnggPSAiZ24xIiwgYnkueSA9ICJnZW5lIikKZWRnZV9jb25kX2RmID0gbWVyZ2UodG1wX2RmLCBsX2NvbmQsIGJ5LnggPSAiZ24yIiwgYnkueSA9ICJnZW5lIikKZWRnZV9jb25kX2RmID0gbWVyZ2UoZWRnZV9jb25kX2RmLCBkYXRhLmZyYW1lKG1heF9jb25kX2N0X2N0KSwgYnkueCA9IDEsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCmVkZ2VfY29uZF9kZiA9IG1lcmdlKGVkZ2VfY29uZF9kZiwgZGF0YS5mcmFtZShtYXhfY29uZF9jdF9jdCksIGJ5LnggPSAyLCBieS55ID0gMCwgYWxsLnggPSBUKQpjb2xuYW1lcyhlZGdlX2NvbmRfZGYpWzk6MTBdID0gYygiY3RfZzEiLCAiY3RfZzIiKQojIGFkZCBoaWdoZXN0IGV4cHJlc3NpbmcgbWFqb3IgY2VsbCB0eXBlcwplZGdlX2NvbmRfZGYkbWFqX2cxID0gaWZlbHNlKGdyZXBsKCJMU0VDIiwgZWRnZV9jb25kX2RmJGN0X2cxKSwgIkVuZG90aGVsaWFsIiwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiSGVwYXRvY3l0ZXMiLCBlZGdlX2NvbmRfZGYkY3RfZzEpLCAiSGVwYXRvY3l0ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiU3RlbGxhdGUgY2VsbHMiLCBlZGdlX2NvbmRfZGYkY3RfZzEpIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIkZpYnJvYmxhc3QiLCBlZGdlX2NvbmRfZGYkY3RfZzEpIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIlZTTUMiLCBlZGdlX2NvbmRfZGYkY3RfZzEpLCAiTWVzZW5jaHltYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkNob2xhbmdpb2N5dGVzIiwgZWRnZV9jb25kX2RmJGN0X2cxKSwgIkNob2xhbmdpb2N5dGVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSW1tdW5lIikpKSkKZWRnZV9jb25kX2RmJG1hal9nMiA9IGlmZWxzZShncmVwbCgiTFNFQyIsIGVkZ2VfY29uZF9kZiRjdF9nMiksICJFbmRvdGhlbGlhbCIsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkhlcGF0b2N5dGVzIiwgZWRnZV9jb25kX2RmJGN0X2cyKSwgIkhlcGF0b2N5dGVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIlN0ZWxsYXRlIGNlbGxzIiwgZWRnZV9jb25kX2RmJGN0X2cyKSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJGaWJyb2JsYXN0IiwgZWRnZV9jb25kX2RmJGN0X2cyKSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJWU01DIiwgZWRnZV9jb25kX2RmJGN0X2cyKSwgIk1lc2VuY2h5bWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJDaG9sYW5naW9jeXRlcyIsIGVkZ2VfY29uZF9kZiRjdF9nMiksICJDaG9sYW5naW9jeXRlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkltbXVuZSIpKSkpCmVkZ2VfY29uZF9kZiA9IG1lcmdlKGVkZ2VfY29uZF9kZiwgZGF0YS5mcmFtZShtYXhfY29uZF9jdF9jb25kKSwgYnkueCA9IDEsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCmVkZ2VfY29uZF9kZiA9IG1lcmdlKGVkZ2VfY29uZF9kZiwgZGF0YS5mcmFtZShtYXhfY29uZF9jdF9jb25kKSwgYnkueCA9IDIsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCgojIGRlZmluZSB0aGUgdmVydGljZXMgb2YgdGhlIG5ldHdvcmsKcG9pbnRfY29uZF9kZiA9IGxfY29uZApwb2ludF9jb25kX2RmJGN0ID0gbWF4X2NvbmRfY3RfY3RbcG9pbnRfY29uZF9kZiRnZW5lXQpwb2ludF9jb25kX2RmJGN0MiA9IGlmZWxzZShncmVwbCgiTFNFQyIsIHBvaW50X2NvbmRfZGYkY3QpLCAiRW5kb3RoZWxpYWwiLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJIZXBhdG9jeXRlcyIsIHBvaW50X2NvbmRfZGYkY3QpLCAiSGVwYXRvY3l0ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiU3RlbGxhdGUgY2VsbHMiLCBwb2ludF9jb25kX2RmJGN0KSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJWU01DIiwgcG9pbnRfY29uZF9kZiRjdCkgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiRmlicm9ibGFzdCIsIHBvaW50X2NvbmRfZGYkY3QpLCAiTWVzZW5jaHltYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkNob2xhbmdpb2N5dGVzIiwgcG9pbnRfY29uZF9kZiRjdCksICJDaG9sYW5naW9jeXRlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkltbXVuZSIpKSkpCnBvaW50X2NvbmRfZGYkY29uZCA9IG1heF9jb25kX2N0X2NvbmRbcG9pbnRfY29uZF9kZiRnZW5lXQoKIyBkZWZpbmUgdGhlIG1lZGlhbiBwb2ludHMgZm9yIGVhY2ggY2VsbCB0eXBlICh1c2luZyBtYXggZXhwcmVzc2lvbikKcGVfbCA9IG1ha2VNZWRpYW4ocG9pbnRfY29uZF9kZiwgZWRnZV9jb25kX2RmLCBjbCA9IGMoImN0IiwgImN0X2cxIiwgImN0X2cyIikpCgojIHBsb3QgdG90YWwgZ2VuZSBjb3JyZWxhdGlvbiBwcm9qZWN0aW9uIGFuZCBtZWRpYW4gbmV0d29yawpwbHRib3RoID0gZ2dwbG90KCkrCiAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfY29uZF9kZiwgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sb3VyID0gY3QyKSwgCiAgICAgICAgICAgICBhbHBoYSA9IDAuMjUsIHNob3cubGVnZW5kID0gRikrCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMCw0LDE5KSkrCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBwZV9sW1syXV0sIAogICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMS54LCB4ZW5kID0gWDEueSwgeSA9IFgyLngsIHllbmQgPSBYMi55LCBzaXplID0gRnJlcSksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuMTUsIHNob3cubGVnZW5kID0gRikrCiAgZ2VvbV9wb2ludChkYXRhID0gcGVfbFtbMV1dLCAKICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGZpbGwgPSBjdCksIAogICAgICAgICAgICAgYWxwaGEgPSAxLCBwY2ggPSAyMSwgc2l6ZSA9IDQpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgNCksIGxpbWl0cyA9IHJhbmdlKHBlX2xbWzJdXSRGcmVxKSkrCiAgdGhlbWVfY2xhc3NpYygpKyB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpwcmludChwbHRib3RoKQoKcGx0Ym90aCA9IGdncGxvdCgpKwogIGdlb21fc2VnbWVudChkYXRhID0gZWRnZV9jb25kX2RmLCAKICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEueCwgeGVuZCA9IFgxLnksIHkgPSBYMi54LCB5ZW5kID0gWDIueSksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuMDMsIHNob3cubGVnZW5kID0gRikrCiAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfY29uZF9kZiwgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sb3VyID0gY3QyKSwgCiAgICAgICAgICAgICBhbHBoYSA9IDAuNiwgc2hvdy5sZWdlbmQgPSBGKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygwLDQsMTkpKSsKICBnZW9tX3BvaW50KGRhdGEgPSBwZV9sW1sxXV0sIAogICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0KSwgCiAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLCA0KSwgbGltaXRzID0gcmFuZ2UocGVfbFtbMl1dJEZyZXEpKSsKICB0aGVtZV9jbGFzc2ljKCkrIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCnByaW50KHBsdGJvdGgpCgojIGdldCBtZWRpYW4gcGVyIGNlbGwgdHlwZSwgcGVyIGNvbmRpdGlvbiAtIEZVTEwgTkVUV09SSwpwZV9jb25kX2wgPSBtYWtlTWVkaWFuQ29uZChwb2ludF9jb25kX2RmLCBlZGdlX2NvbmRfZGYsIGNsID0gYygiY3QiLCAiY3RfZzEiLCAiY3RfZzIiKSkKCnBsdF9jb25kX2wgPSBsaXN0KCkKZm9yKGNjIGluIHVuaXF1ZShwZV9jb25kX2xbWzJdXSRjb25kaXRpb24pKXsKICBwbHRfY29uZF9sW1tjY11dID0gZ2dwbG90KCkrCiAgICBnZW9tX3NlZ21lbnQoZGF0YSA9IHBlX2NvbmRfbFtbMl1dW3BlX2NvbmRfbFtbMl1dJGNvbmRpdGlvbj09Y2MsXSwgCiAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEueCwgeGVuZCA9IFgxLnksIHkgPSBYMi54LCB5ZW5kID0gWDIueSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gRnJlcSwgYWxwaGEgPSBGcmVxKSkrCiAgICBnZW9tX3BvaW50KGRhdGEgPSBwZV9jb25kX2xbWzFdXSwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGZpbGwgPSBjdCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpLCBsaW1pdHMgPSByYW5nZShwZV9jb25kX2xbWzJdXSRGcmVxKSkrCiAgICBzY2FsZV9hbHBoYV9jb250aW51b3VzKGxpbWl0cyA9IHJhbmdlKHBlX2NvbmRfbFtbMl1dJEZyZXEpKSsKICAgIGxhYnModGl0bGUgPSBjYykrCiAgICBndWlkZXMoc2l6ZSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSwKICAgICAgICAgICBhbHBoYSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSkrCiAgICB0aGVtZV9jbGFzc2ljKCkKfQpwbHRfY29uZF9sW1sibGVnIl1dID0gY293cGxvdDo6Z2V0X2xlZ2VuZChwbHRfY29uZF9sW1siaGVhbHRoeSJdXSkKCiMgcGxvdCBGVUxMIE5FVFdPUksgbWVkaWFuIHBlciBjb25kaXRpb24KY293cGxvdDo6cGxvdF9ncmlkKHBsdF9jb25kX2xbWzFdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCAKICAgICAgICAgICAgICAgICAgIHBsdF9jb25kX2xbWzJdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogICAgICAgICAgICAgICAgICAgcGx0X2NvbmRfbFtbM11dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIHBsdF9jb25kX2wkbGVnLCAKICAgICAgICAgICAgICAgICAgIG5jb2wgPSA0LCByZWxfd2lkdGhzID0gYygxLDEsMSwwLjUpKQoKIyBnZXQgbWVkaWFuIHBlciBjZWxsIHR5cGUsIHBlciBjb25kaXRpb24gLSBVTklRVUUgUEVSIENPTkRUSU9OIE5FVFdPUksKcGVfY29uZF9sX3UgPSBtYWtlTWVkaWFuQ29uZChwb2ludF9jb25kX2RmLCBlZGdlX2NvbmRfZGZbZWRnZV9jb25kX2RmJGlkX2NwX2ludGVyYWN0aW9uICVpbiUgdW5pcXVlX2ludGVycyxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsID0gYygiY3QiLCAiY3RfZzEiLCAiY3RfZzIiKSkKCnBsdF9jb25kX2xfdSA9IGxpc3QoKQpmb3IoY2MgaW4gdW5pcXVlKHBlX2NvbmRfbFtbMl1dJGNvbmRpdGlvbikpewogIHBsdF9jb25kX2xfdVtbY2NdXSA9IGdncGxvdCgpKwogICAgZ2VvbV9zZWdtZW50KGRhdGEgPSBwZV9jb25kX2xfdVtbMl1dW3BlX2NvbmRfbF91W1syXV0kY29uZGl0aW9uPT1jYyxdLCAKICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMS54LCB4ZW5kID0gWDEueSwgeSA9IFgyLngsIHllbmQgPSBYMi55LCBzaXplID0gRnJlcSwgYWxwaGEgPSBGcmVxKSkrCiAgICBnZW9tX3BvaW50KGRhdGEgPSBwZV9jb25kX2xfdVtbMV1dLCAKICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0KSwgCiAgICAgICAgICAgICAgIGFscGhhID0gMSwgcGNoID0gMjEsIHNpemUgPSA0KSsKICAgIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgNCksIGxpbWl0cyA9IHJhbmdlKHBlX2NvbmRfbF91W1syXV0kRnJlcSkpKwogICAgc2NhbGVfYWxwaGFfY29udGludW91cyhsaW1pdHMgPSByYW5nZShwZV9jb25kX2xfdVtbMl1dJEZyZXEpKSsKICAgIGxhYnModGl0bGUgPSBjYykrCiAgICBndWlkZXMoc2l6ZSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSwKICAgICAgICAgICBhbHBoYSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSkrCiAgICB0aGVtZV9jbGFzc2ljKCkKfQpwbHRfY29uZF9sX3VbWyJsZWciXV0gPSBjb3dwbG90OjpnZXRfbGVnZW5kKHBsdF9jb25kX2xfdVtbImhlYWx0aHkiXV0pCgojIHBsb3QgVU5JUVVFIFBFUiBDT05EVElPTiBORVRXT1JLIG1lZGlhbiBwZXIgY29uZGl0aW9uCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbHRfY29uZF9sX3VbWzFdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCAKICAgICAgICAgICAgICAgICAgIHBsdF9jb25kX2xfdVtbMl1dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCiAgICAgICAgICAgICAgICAgICBwbHRfY29uZF9sX3VbWzNdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCBwbHRfY29uZF9sX3UkbGVnLCAKICAgICAgICAgICAgICAgICAgIG5jb2wgPSA0LCByZWxfd2lkdGhzID0gYygxLDEsMSwwLjUpKQoKIyBnZXQgbWVkaWFuIHBlciBjZWxsIHR5cGUsIHBlciBjb25kaXRpb24gLSBIRUFMVEhZIE5FVFdPUksKcGVfY29uZF9sX2ggPSBtYWtlTWVkaWFuQ29uZChwb2ludF9jb25kX2RmLCBlZGdlX2NvbmRfZGZbZWRnZV9jb25kX2RmJGlkX2NwX2ludGVyYWN0aW9uICVpbiUgaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbixdLCBjbCA9IGMoImN0IiwgImN0X2cxIiwgImN0X2cyIikpCgpwbHRfY29uZF9sX2ggPSBsaXN0KCkKZm9yKGNjIGluIHVuaXF1ZShwZV9jb25kX2xbWzJdXSRjb25kaXRpb24pKXsKICBwbHRfY29uZF9sX2hbW2NjXV0gPSBnZ3Bsb3QoKSsKICAgIGdlb21fc2VnbWVudChkYXRhID0gcGVfY29uZF9sX2hbWzJdXVtwZV9jb25kX2xfaFtbMl1dJGNvbmRpdGlvbj09Y2MsXSwgCiAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEueCwgeGVuZCA9IFgxLnksIHkgPSBYMi54LCB5ZW5kID0gWDIueSwgc2l6ZSA9IEZyZXEsIGFscGhhID0gRnJlcSkpKwogICAgZ2VvbV9wb2ludChkYXRhID0gcGVfY29uZF9sX2hbWzFdXSwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGZpbGwgPSBjdCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpLCBsaW1pdHMgPSByYW5nZShwZV9jb25kX2xfaFtbMl1dJEZyZXEpKSsKICAgIHNjYWxlX2FscGhhX2NvbnRpbnVvdXMobGltaXRzID0gcmFuZ2UocGVfY29uZF9sX2hbWzJdXSRGcmVxKSkrCiAgICBsYWJzKHRpdGxlID0gY2MpKwogICAgZ3VpZGVzKHNpemUgPSBndWlkZV9sZWdlbmQoZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBucm93ID0gMiksCiAgICAgICAgICAgYWxwaGEgPSBndWlkZV9sZWdlbmQoZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBucm93ID0gMikpKwogICAgdGhlbWVfY2xhc3NpYygpCn0KcGx0X2NvbmRfbF9oW1sibGVnIl1dID0gY293cGxvdDo6Z2V0X2xlZ2VuZChwbHRfY29uZF9sX2hbWyJoZWFsdGh5Il1dKQoKIyBwbG90IEhFQUxUSFkgTkVUV09SSyBtZWRpYW4gcGVyIGNvbmRpdGlvbgpjb3dwbG90OjpwbG90X2dyaWQocGx0X2NvbmRfbF9oW1sxXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgICAgICAgICAgICAgICAgICBwbHRfY29uZF9sX2hbWzJdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogICAgICAgICAgICAgICAgICAgcGx0X2NvbmRfbF9oW1szXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgcGx0X2NvbmRfbF9oJGxlZywgCiAgICAgICAgICAgICAgICAgICBuY29sID0gNCwgcmVsX3dpZHRocyA9IGMoMSwxLDEsMC41KSkKCiMgZ2V0IG1lZGlhbiBwZXIgY2VsbCB0eXBlLCBwZXIgY29uZGl0aW9uIC0gSEVBTFRIWSBDT01QQVJJU09OIE5FVFdPUksKcGVfY29uZF9sX2NoID0gbWFrZU1lZGlhbkNvbmQocG9pbnRfY29uZF9kZiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWRnZV9jb25kX2RmW2VkZ2VfY29uZF9kZiRpZF9jcF9pbnRlcmFjdGlvbiAlaW4lIGNvbXBoX2ludGVycyxdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbCA9IGMoImN0IiwgImN0X2cxIiwgImN0X2cyIikpCgpwbHRfY29uZF9sX2NoID0gbGlzdCgpCmZvcihjYyBpbiB1bmlxdWUocGVfY29uZF9sW1syXV0kY29uZGl0aW9uKSl7CiAgcGx0X2NvbmRfbF9jaFtbY2NdXSA9IGdncGxvdCgpKwogICAgZ2VvbV9zZWdtZW50KGRhdGEgPSBwZV9jb25kX2xfY2hbWzJdXVtwZV9jb25kX2xfY2hbWzJdXSRjb25kaXRpb249PWNjLF0sIAogICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnksIHNpemUgPSBGcmVxLCBhbHBoYSA9IEZyZXEpKSsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHBlX2NvbmRfbF9jaFtbMV1dLCAKICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0KSwgCiAgICAgICAgICAgICAgIGFscGhhID0gMSwgcGNoID0gMjEsIHNpemUgPSA0KSsKICAgIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgNCksIGxpbWl0cyA9IHJhbmdlKHBlX2NvbmRfbF9jaFtbMl1dJEZyZXEpKSsKICAgIHNjYWxlX2FscGhhX2NvbnRpbnVvdXMobGltaXRzID0gcmFuZ2UocGVfY29uZF9sX2NoW1syXV0kRnJlcSkpKwogICAgbGFicyh0aXRsZSA9IGNjKSsKICAgIGd1aWRlcyhzaXplID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpLAogICAgICAgICAgIGFscGhhID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpKSsKICAgIHRoZW1lX2NsYXNzaWMoKQp9CnBsdF9jb25kX2xfY2hbWyJsZWciXV0gPSBjb3dwbG90OjpnZXRfbGVnZW5kKHBsdF9jb25kX2xfY2hbWyJoZWFsdGh5Il1dKQoKIyBwbG90IEhFQUxUSFkgQ09NUEFSSVNPTiBORVRXT1JLIG1lZGlhbiBwZXIgY29uZGl0aW9uCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbHRfY29uZF9sX2NoW1sxXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgICAgICAgICAgICAgICAgICBwbHRfY29uZF9sX2NoW1syXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwKICAgICAgICAgICAgICAgICAgIHBsdF9jb25kX2xfY2hbWzNdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCBwbHRfY29uZF9sX2NoJGxlZywgCiAgICAgICAgICAgICAgICAgICBuY29sID0gMiwgcmVsX3dpZHRocyA9IGMoMSwxLDEsMC41KSkKYGBgCgpNRFMgd2l0aCBzaW1wbGlmaWVkIGxhYmVscwoKYGBge3J9CiMgYWRkIGxhYmVscyB0byBwb2ludHMKcG9pbnRfY29uZF9kZiRjdF9taWQgPSBwb2ludF9jb25kX2RmJGN0CnBvaW50X2NvbmRfZGYkY3RfbWlkW2dyZXBsKCJOSyIsIHBvaW50X2NvbmRfZGYkY3QpXSA9ICJJTEMiCnBvaW50X2NvbmRfZGYkY3RfbWlkW2dyZXBsKCJJTEMiLCBwb2ludF9jb25kX2RmJGN0KV0gPSAiSUxDIgpwb2ludF9jb25kX2RmJGN0X21pZFtncmVwbCgiVCBjZWxscyIsIHBvaW50X2NvbmRfZGYkY3QpXSA9ICJUIGNlbGxzIgpwb2ludF9jb25kX2RmJGN0X21pZFtncmVwbCgiTUFJVCIsIHBvaW50X2NvbmRfZGYkY3QpXSA9ICJUIGNlbGxzIgpwb2ludF9jb25kX2RmJGN0X21pZFtncmVwbCgiVHJlZyIsIHBvaW50X2NvbmRfZGYkY3QpXSA9ICJUIGNlbGxzIgpwb2ludF9jb25kX2RmJGN0X21pZFtncmVwbCgiU3RlbGxhdGUiLCBwb2ludF9jb25kX2RmJGN0KV0gPSAiTWVzZW5jaHltYWwiCnBvaW50X2NvbmRfZGYkY3RfbWlkW2dyZXBsKCJGaWJyb2JsYXN0IiwgcG9pbnRfY29uZF9kZiRjdCldID0gIk1lc2VuY2h5bWFsIgpwb2ludF9jb25kX2RmJGN0X21pZFtncmVwbCgiVlNNQyIsIHBvaW50X2NvbmRfZGYkY3QpXSA9ICJNZXNlbmNoeW1hbCIKcG9pbnRfY29uZF9kZiRjdF9taWRbZ3JlcGwoIkxTRUMiLCBwb2ludF9jb25kX2RmJGN0KV0gPSAiTFNFQyIKcG9pbnRfY29uZF9kZiRjdF9taWRbZ3JlcGwoIm5vbi1MU0VDIiwgcG9pbnRfY29uZF9kZiRjdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKV0gPSAib3RoZXIgRUNzIgpwb2ludF9jb25kX2RmJGN0X21pZFtncmVwbCgiTHltcGhhdGljIEVDIiwgcG9pbnRfY29uZF9kZiRjdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKV0gPSAib3RoZXIgRUNzIgpwb2ludF9jb25kX2RmJGN0X21pZFtncmVwbCgiRGl2aWRpbmcgZW5kb3RoZWxpYWwgY2VsbHMiLCBwb2ludF9jb25kX2RmJGN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXhlZCA9IFQpXSA9ICJvdGhlciBFQ3MiCnBvaW50X2NvbmRfZGYkY3RfbWlkW2dyZXBsKCJNb25vY3l0ZXMiLCBwb2ludF9jb25kX2RmJGN0KV0gPSAib3RoZXIgTW9uby1NYWMiCnBvaW50X2NvbmRfZGYkY3RfbWlkW2dyZXBsKCJNYWNyb3BoYWdlcyIsIHBvaW50X2NvbmRfZGYkY3QpXSA9ICJvdGhlciBNb25vLU1hYyIKcG9pbnRfY29uZF9kZiRjdF9taWRbZ3JlcGwoImNEQyIsIHBvaW50X2NvbmRfZGYkY3QpXSA9ICJvdGhlciBNb25vLU1hYyIKcG9pbnRfY29uZF9kZiRjdF9taWRbZ3JlcGwoImFjdGl2YXRlZCBEQ3MiLCBwb2ludF9jb25kX2RmJGN0KV0gPSAib3RoZXIgTW9uby1NYWMiCnBvaW50X2NvbmRfZGYkY3RfbWlkW2dyZXBsKCJLdXBmZmVyIiwgcG9pbnRfY29uZF9kZiRjdCldID0gIkt1cGZmZXIgY2VsbHMiCnBvaW50X2NvbmRfZGYkY3RfbWlkW2dyZXBsKCJQbGFzbWEiLCBwb2ludF9jb25kX2RmJGN0KV0gPSAiQiBjZWxscyIKCiMgYWRkIGxhYmVscyB0byBlZGdlcwptYXRjaF9kZiA9IHVuaXF1ZShkYXRhLmZyYW1lKCJjdCIgPSBwb2ludF9jb25kX2RmJGN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdF9taWQiID0gcG9pbnRfY29uZF9kZiRjdF9taWQpKQpyb3duYW1lcyhtYXRjaF9kZikgPSBtYXRjaF9kZiRjdAoKZWRnZV9jb25kX2RmJG1pZF9nMSA9IG1hdGNoX2RmW2VkZ2VfY29uZF9kZiRjdF9nMSwiY3RfbWlkIl0KZWRnZV9jb25kX2RmJG1pZF9nMiA9IG1hdGNoX2RmW2VkZ2VfY29uZF9kZiRjdF9nMiwiY3RfbWlkIl0KCnBlX21pZF9sID0gbWFrZU1lZGlhbihwb2ludF9jb25kX2RmLCBlZGdlX2NvbmRfZGYsIAogICAgICAgICAgICAgICAgICAgICAgY2wgPSBjKCJjdF9taWQiLCAibWlkX2cxIiwgIm1pZF9nMiIpKQoKIyBwbG90IHRvdGFsIGdlbmUgY29ycmVsYXRpb24gcHJvamVjdGlvbiBhbmQgbWVkaWFuIG5ldHdvcmsKcGx0Ym90aCA9IGdncGxvdCgpKwogIGdlb21fcG9pbnQoZGF0YSA9IHBvaW50X2NvbmRfZGYsIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbG91ciA9IGN0MiksIAogICAgICAgICAgICAgYWxwaGEgPSAwLjI1LCBzaG93LmxlZ2VuZCA9IEYpKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDAsNCwxOSkpKwogIGdlb21fc2VnbWVudChkYXRhID0gcGVfbWlkX2xbWzJdXSwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnksIHNpemUgPSBGcmVxKSwgCiAgICAgICAgICAgICAgIGFscGhhID0gMC4xNSwgc2hvdy5sZWdlbmQgPSBGKSsKICBnZW9tX3BvaW50KGRhdGEgPSBwZV9taWRfbFtbMV1dLCAKICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGZpbGwgPSBjdF9taWQpLCAKICAgICAgICAgICAgIGFscGhhID0gMSwgcGNoID0gMjEsIHNpemUgPSA0KSsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpKSsKICB0aGVtZV9jbGFzc2ljKCkrIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCnByaW50KHBsdGJvdGgpCgojIGdldCBtZWRpYW4gcGVyIGNlbGwgdHlwZSwgcGVyIGNvbmRpdGlvbiAtIEhFQUxUSFkgQ09NUEFSSVNPTiBORVRXT1JLCnBlX2NvbmRfbF9taWQgPSBtYWtlTWVkaWFuQ29uZChwb2ludF9jb25kX2RmLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZGdlX2NvbmRfZGZbZWRnZV9jb25kX2RmJGlkX2NwX2ludGVyYWN0aW9uJWluJWNvbXBoX2ludGVycyxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkZ2VfYnkgPSAiY29uZGl0aW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbCA9IGMoImN0X21pZCIsICJtaWRfZzEiLCAibWlkX2cyIikpCgpwbHRfY29uZF9sX2NoID0gbGlzdCgpCmZvcihjYyBpbiB1bmlxdWUocGVfY29uZF9sW1syXV0kY29uZGl0aW9uKSl7CiAgcGx0X2NvbmRfbF9jaFtbY2NdXSA9IGdncGxvdCgpKwogICAgZ2VvbV9zZWdtZW50KGRhdGEgPSBwZV9jb25kX2xfbWlkW1syXV1bcGVfY29uZF9sX21pZFtbMl1dJGNvbmRpdGlvbj09Y2MsXSwgCiAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEueCwgeGVuZCA9IFgxLnksIHkgPSBYMi54LCB5ZW5kID0gWDIueSwgc2l6ZSA9IEZyZXEsIGFscGhhID0gRnJlcSkpKwogICAgZ2VvbV9wb2ludChkYXRhID0gcGVfY29uZF9sX21pZFtbMV1dLCAKICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0X21pZCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpLCBsaW1pdHMgPSByYW5nZShwZV9jb25kX2xfbWlkW1syXV0kRnJlcSkpKwogICAgc2NhbGVfYWxwaGFfY29udGludW91cyhsaW1pdHMgPSByYW5nZShwZV9jb25kX2xfbWlkW1syXV0kRnJlcSkpKwogICAgbGFicyh0aXRsZSA9IGNjKSsKICAgIGd1aWRlcyhzaXplID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpLAogICAgICAgICAgIGFscGhhID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpKSsKICAgIHRoZW1lX2NsYXNzaWMoKQp9CnBsdF9jb25kX2xfY2hbWyJsZWciXV0gPSBjb3dwbG90OjpnZXRfbGVnZW5kKHBsdF9jb25kX2xfY2hbWyJoZWFsdGh5Il1dKQoKIyBwbG90IEhFQUxUSFkgQ09NUEFSSVNPTiBORVRXT1JLIG1lZGlhbiBwZXIgY29uZGl0aW9uCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbHRfY29uZF9sX2NoW1sxXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgICAgICAgICAgICAgICAgICBwbHRfY29uZF9sX2NoW1syXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwKICAgICAgICAgICAgICAgICAgIHBsdF9jb25kX2xfY2hbWzNdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCBwbHRfY29uZF9sX2NoJGxlZywgCiAgICAgICAgICAgICAgICAgICBuY29sID0gNCwgcmVsX3dpZHRocyA9IGMoMSwxLDEsMC41KSkKYGBgCgpTYXZlIG5ldHdvcmsgb2JqZWN0cwoKYGBge3J9CnNhdmVSRFMoY29tcGhfaW50ZXJzLCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3YzL2NvbXBoX2ludGVycy5SRFMiKQpzYXZlKGVkZ2VfY29uZF9kZiwgcG9pbnRfY29uZF9kZiwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS92My9uZXR3b3Jrc19jb25kLlJEYXRhIikKc2F2ZShwZV9sLCBwZV9jb25kX2wsIHBlX2NvbmRfbF91LCBwZV9jb25kX2xfaCwgcGVfY29uZF9sX2NoLCAKICAgICBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3YzL21lZGlhbl9uZXR3b3Jrc19jb25kLlJEYXRhIikKc2F2ZShwZV9taWRfbCwgcGVfY29uZF9sX21pZCwgCiAgICAgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS92My9tZWRpYW5fbmV0d29ya3NfY29uZF9taWRjdC5SRGF0YSIpCmBgYAoKUGxvdCBsaWdhbmRzIGFuZCByZWNlcHRvcnMgd2l0aCBVTUFQCgpgYGB7cn0Kc2V0LnNlZWQoMjk1NCkKbCA9IHV3b3Q6OnVtYXAodChtZWFuX2V4cF9jb25kX2xyKSwgbWV0cmljID0gImNvc2luZSIsIHJldF9ubiA9IFQsIG5fZXBvY2hzID0gMTAwMCkKbF9jb25kID0gZGF0YS5mcmFtZShsJGVtYmVkZGluZykKbF9jb25kJGdlbmUgPSBjb2xuYW1lcyhtZWFuX2V4cF9jb25kX2xyKQpyb3duYW1lcyhsX2NvbmQpID0gY29sbmFtZXMobWVhbl9leHBfY29uZF9scikKdG1wX2RmID0gbWVyZ2UoZ2VuZV9wYWlyc19jb25kLCBsX2NvbmQsIGJ5LnggPSAiZ24xIiwgYnkueSA9ICJnZW5lIikKZWRnZV9jb25kX3VtYXBfZGYgPSBtZXJnZSh0bXBfZGYsIGxfY29uZCwgYnkueCA9ICJnbjIiLCBieS55ID0gImdlbmUiKQplZGdlX2NvbmRfdW1hcF9kZiA9IG1lcmdlKGVkZ2VfY29uZF91bWFwX2RmLCBkYXRhLmZyYW1lKG1heF9jb25kX2N0X2N0KSwgYnkueCA9IDEsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCmVkZ2VfY29uZF91bWFwX2RmID0gbWVyZ2UoZWRnZV9jb25kX3VtYXBfZGYsIGRhdGEuZnJhbWUobWF4X2NvbmRfY3RfY3QpLCBieS54ID0gMiwgYnkueSA9IDAsIGFsbC54ID0gVCkKY29sbmFtZXMoZWRnZV9jb25kX3VtYXBfZGYpWzk6MTBdID0gYygiY3RfZzEiLCAiY3RfZzIiKQplZGdlX2NvbmRfdW1hcF9kZiRtYWpfZzEgPSBpZmVsc2UoZ3JlcGwoIkxTRUMiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMSksICJFbmRvdGhlbGlhbCIsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkhlcGF0b2N5dGVzIiwgZWRnZV9jb25kX3VtYXBfZGYkY3RfZzEpLCAiSGVwYXRvY3l0ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiU3RlbGxhdGUgY2VsbHMiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMSkgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiVlNNQyIsIGVkZ2VfY29uZF91bWFwX2RmJGN0X2cxKSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJGaWJyb2JsYXN0IiwgZWRnZV9jb25kX3VtYXBfZGYkY3RfZzEpLCAiTWVzZW5jaHltYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkNob2xhbmdpb2N5dGVzIiwgZWRnZV9jb25kX3VtYXBfZGYkY3RfZzEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNob2xhbmdpb2N5dGVzIiwgIkltbXVuZSIpKSkpCmVkZ2VfY29uZF91bWFwX2RmJG1hal9nMiA9IGlmZWxzZShncmVwbCgiTFNFQyIsIGVkZ2VfY29uZF91bWFwX2RmJGN0X2cyKSwgIkVuZG90aGVsaWFsIiwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiSGVwYXRvY3l0ZXMiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMiksICJIZXBhdG9jeXRlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJTdGVsbGF0ZSBjZWxscyIsIGVkZ2VfY29uZF91bWFwX2RmJGN0X2cyKSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJWU01DIiwgZWRnZV9jb25kX3VtYXBfZGYkY3RfZzIpIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIkZpYnJvYmxhc3QiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMiksICJNZXNlbmNoeW1hbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiQ2hvbGFuZ2lvY3l0ZXMiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hvbGFuZ2lvY3l0ZXMiLCAiSW1tdW5lIikpKSkKZWRnZV9jb25kX3VtYXBfZGYgPSBtZXJnZShlZGdlX2NvbmRfdW1hcF9kZiwgZGF0YS5mcmFtZShtYXhfY29uZF9jdF9jb25kKSwgYnkueCA9IDEsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCmVkZ2VfY29uZF91bWFwX2RmID0gbWVyZ2UoZWRnZV9jb25kX3VtYXBfZGYsIGRhdGEuZnJhbWUobWF4X2NvbmRfY3RfY29uZCksIGJ5LnggPSAyLCBieS55ID0gMCwgYWxsLnggPSBUKQpjb2xuYW1lcyhlZGdlX2NvbmRfdW1hcF9kZilbNF0gPSAiY29uZCIKCnBvaW50X2NvbmRfdW1hcF9kZiA9IGxfY29uZApwb2ludF9jb25kX3VtYXBfZGYkY3QgPSBtYXhfY29uZF9jdF9jdFtwb2ludF9jb25kX3VtYXBfZGYkZ2VuZV0KcG9pbnRfY29uZF91bWFwX2RmJGN0MiA9IGlmZWxzZShncmVwbCgiTFNFQyIsIHBvaW50X2NvbmRfdW1hcF9kZiRjdCksICJFbmRvdGhlbGlhbCIsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkhlcGF0b2N5dGVzIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KSwgIkhlcGF0b2N5dGVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIlN0ZWxsYXRlIGNlbGxzIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJWU01DIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJGaWJyb2JsYXN0IiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KSwgIk1lc2VuY2h5bWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJDaG9sYW5naW9jeXRlcyIsIHBvaW50X2NvbmRfdW1hcF9kZiRjdCksICJDaG9sYW5naW9jeXRlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkltbXVuZSIpKSkpCnBvaW50X2NvbmRfdW1hcF9kZiRjb25kID0gbWF4X2NvbmRfY3RfY29uZFtwb2ludF9jb25kX3VtYXBfZGYkZ2VuZV0KCgpwZV91bWFwX2wgPSBtYWtlTWVkaWFuKHBvaW50X2NvbmRfdW1hcF9kZiwgZWRnZV9jb25kX3VtYXBfZGYsIGNsID0gYygiY3QiLCAiY3RfZzEiLCAiY3RfZzIiKSkKCiMgcGxvdCB0b3RhbCBnZW5lIGNvcnJlbGF0aW9uIHByb2plY3Rpb24gYW5kIG1lZGlhbiBuZXR3b3JrCnBsdGJvdGggPSBnZ3Bsb3QoKSsKICBnZW9tX3BvaW50KGRhdGEgPSBwb2ludF9jb25kX3VtYXBfZGYsIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbG91ciA9IGN0MiksIAogICAgICAgICAgICAgYWxwaGEgPSAwLjI1LCBzaG93LmxlZ2VuZCA9IEYpKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDAsNCwxOSkpKwogIGdlb21fc2VnbWVudChkYXRhID0gcGVfdW1hcF9sW1syXV0sIAogICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMS54LCB4ZW5kID0gWDEueSwgeSA9IFgyLngsIHllbmQgPSBYMi55LCBzaXplID0gRnJlcSksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuMTUsIHNob3cubGVnZW5kID0gRikrCiAgZ2VvbV9wb2ludChkYXRhID0gcGVfdW1hcF9sW1sxXV0sIAogICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0KSwgCiAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLCA0KSwgbGltaXRzID0gcmFuZ2UocGVfbFtbMl1dJEZyZXEpKSsKICB0aGVtZV9jbGFzc2ljKCkrIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCnByaW50KHBsdGJvdGgpCgpwbHRib3RoID0gZ2dwbG90KCkrCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBlZGdlX2NvbmRfdW1hcF9kZiwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnkpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjAzLCBzaG93LmxlZ2VuZCA9IEYpKwogIGdlb21fcG9pbnQoZGF0YSA9IHBvaW50X2NvbmRfdW1hcF9kZiwgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sb3VyID0gY3QyKSwgCiAgICAgICAgICAgICBhbHBoYSA9IDAuNiwgc2hvdy5sZWdlbmQgPSBGKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygwLDQsMTkpKSsKICBnZW9tX3BvaW50KGRhdGEgPSBwZV91bWFwX2xbWzFdXSwgCiAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMSwgeSA9IFgyLCBmaWxsID0gY3QpLCAKICAgICAgICAgICAgIGFscGhhID0gMSwgcGNoID0gMjEsIHNpemUgPSA0KSsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpLCBsaW1pdHMgPSByYW5nZShwZV9sW1syXV0kRnJlcSkpKwogIHRoZW1lX2NsYXNzaWMoKSsgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkKcHJpbnQocGx0Ym90aCkKCiMgZ2V0IG1lZGlhbiBwZXIgY2VsbCB0eXBlLCBwZXIgY29uZGl0aW9uIC0gSEVBTFRIWSBDT01QQVJJU09OIE5FVFdPUksKcGVfdW1hcF9jb25kX2xfY2ggPSBtYWtlTWVkaWFuQ29uZChwb2ludF9jb25kX3VtYXBfZGYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkZ2VfY29uZF91bWFwX2RmW2VkZ2VfY29uZF91bWFwX2RmJGlkX2NwX2ludGVyYWN0aW9uJWluJWNvbXBoX2ludGVycyxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkZ2VfYnkgPSAiY29uZCIsIGNsID0gYygiY3QiLCAiY3RfZzEiLCAiY3RfZzIiKSkKCnBsdF91bWFwX2NvbmRfbF9jaCA9IGxpc3QoKQpmb3IoY2MgaW4gdW5pcXVlKHBlX2NvbmRfbFtbMl1dJGNvbmQpKXsKICBwbHRfdW1hcF9jb25kX2xfY2hbW2NjXV0gPSBnZ3Bsb3QoKSsKICAgIGdlb21fc2VnbWVudChkYXRhID0gcGVfdW1hcF9jb25kX2xfY2hbWzJdXVtwZV91bWFwX2NvbmRfbF9jaFtbMl1dJGNvbmQ9PWNjLF0sIAogICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnksIHNpemUgPSBGcmVxLCBhbHBoYSA9IEZyZXEpKSsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHBlX3VtYXBfY29uZF9sX2NoW1sxXV0sIAogICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMSwgeSA9IFgyLCBmaWxsID0gY3QpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAxLCBwY2ggPSAyMSwgc2l6ZSA9IDQpKwogICAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLCA0KSwgbGltaXRzID0gcmFuZ2UocGVfdW1hcF9jb25kX2xfY2hbWzJdXSRGcmVxKSkrCiAgICBzY2FsZV9hbHBoYV9jb250aW51b3VzKGxpbWl0cyA9IHJhbmdlKHBlX3VtYXBfY29uZF9sX2NoW1syXV0kRnJlcSkpKwogICAgbGFicyh0aXRsZSA9IGNjKSsKICAgIGd1aWRlcyhzaXplID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpLAogICAgICAgICAgIGFscGhhID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpKSsKICAgIHRoZW1lX2NsYXNzaWMoKQp9CnBsdF91bWFwX2NvbmRfbF9jaFtbImxlZyJdXSA9IGNvd3Bsb3Q6OmdldF9sZWdlbmQocGx0X3VtYXBfY29uZF9sX2NoW1siaGVhbHRoeSJdXSkKCiMgcGxvdCBIRUFMVEhZIENPTVBBUklTT04gTkVUV09SSyBtZWRpYW4gcGVyIGNvbmRpdGlvbgpjb3dwbG90OjpwbG90X2dyaWQocGx0X3VtYXBfY29uZF9sX2NoW1sxXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgICAgICAgICAgICAgICAgICBwbHRfdW1hcF9jb25kX2xfY2hbWzJdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogICAgICAgICAgICAgICAgICAgcGx0X3VtYXBfY29uZF9sX2NoW1szXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgcGx0X3VtYXBfY29uZF9sX2NoJGxlZywgCiAgICAgICAgICAgICAgICAgICBuY29sID0gNCwgcmVsX3dpZHRocyA9IGMoMSwxLDEsMC41KSkKYGBgCgpVTUFQcyB3aXRoIHNpbXBsaWZpZWQgbGFiZWxzCgpgYGB7cn0KIyBhZGQgbGFiZWxzIHRvIHBvaW50cwpwb2ludF9jb25kX3VtYXBfZGYkY3RfbWlkID0gcG9pbnRfY29uZF91bWFwX2RmJGN0CnBvaW50X2NvbmRfdW1hcF9kZiRjdF9taWRbZ3JlcGwoIk5LIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KV0gPSAiSUxDIgpwb2ludF9jb25kX3VtYXBfZGYkY3RfbWlkW2dyZXBsKCJJTEMiLCBwb2ludF9jb25kX3VtYXBfZGYkY3QpXSA9ICJJTEMiCnBvaW50X2NvbmRfdW1hcF9kZiRjdF9taWRbZ3JlcGwoIlQgY2VsbHMiLCBwb2ludF9jb25kX3VtYXBfZGYkY3QpXSA9ICJUIGNlbGxzIgpwb2ludF9jb25kX3VtYXBfZGYkY3RfbWlkW2dyZXBsKCJNQUlUIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KV0gPSAiVCBjZWxscyIKcG9pbnRfY29uZF91bWFwX2RmJGN0X21pZFtncmVwbCgiVHJlZyIsIHBvaW50X2NvbmRfdW1hcF9kZiRjdCldID0gIlQgY2VsbHMiCnBvaW50X2NvbmRfdW1hcF9kZiRjdF9taWRbZ3JlcGwoIlN0ZWxsYXRlIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KV0gPSAiTWVzZW5jaHltYWwiCnBvaW50X2NvbmRfdW1hcF9kZiRjdF9taWRbZ3JlcGwoIlZTTUMiLCBwb2ludF9jb25kX3VtYXBfZGYkY3QpXSA9ICJNZXNlbmNoeW1hbCIKcG9pbnRfY29uZF91bWFwX2RmJGN0X21pZFtncmVwbCgiRmlicm9ibGFzdCIsIHBvaW50X2NvbmRfdW1hcF9kZiRjdCldID0gIk1lc2VuY2h5bWFsIgpwb2ludF9jb25kX3VtYXBfZGYkY3RfbWlkW2dyZXBsKCJMU0VDIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KV0gPSAiTFNFQyIKcG9pbnRfY29uZF91bWFwX2RmJGN0X21pZFtncmVwbCgibm9uLUxTRUMiLCBwb2ludF9jb25kX3VtYXBfZGYkY3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVCldID0gIm90aGVyIEVDcyIKcG9pbnRfY29uZF91bWFwX2RmJGN0X21pZFtncmVwbCgiTHltcGhhdGljIEVDIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXhlZCA9IFQpXSA9ICJvdGhlciBFQ3MiCnBvaW50X2NvbmRfdW1hcF9kZiRjdF9taWRbZ3JlcGwoIkRpdmlkaW5nIGVuZG90aGVsaWFsIGNlbGxzIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXhlZCA9IFQpXSA9ICJvdGhlciBFQ3MiCnBvaW50X2NvbmRfdW1hcF9kZiRjdF9taWRbZ3JlcGwoIk1vbm9jeXRlcyIsIHBvaW50X2NvbmRfdW1hcF9kZiRjdCldID0gIm90aGVyIE1vbm8tTWFjIgpwb2ludF9jb25kX3VtYXBfZGYkY3RfbWlkW2dyZXBsKCJNYWNyb3BoYWdlcyIsIHBvaW50X2NvbmRfdW1hcF9kZiRjdCldID0gIm90aGVyIE1vbm8tTWFjIgpwb2ludF9jb25kX3VtYXBfZGYkY3RfbWlkW2dyZXBsKCJjREMiLCBwb2ludF9jb25kX3VtYXBfZGYkY3QpXSA9ICJvdGhlciBNb25vLU1hYyIKcG9pbnRfY29uZF91bWFwX2RmJGN0X21pZFtncmVwbCgiYWN0aXZhdGVkIERDcyIsIHBvaW50X2NvbmRfdW1hcF9kZiRjdCldID0gIm90aGVyIE1vbm8tTWFjIgpwb2ludF9jb25kX3VtYXBfZGYkY3RfbWlkW2dyZXBsKCJLdXBmZmVyIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KV0gPSAiS3VwZmZlciBjZWxscyIKcG9pbnRfY29uZF91bWFwX2RmJGN0X21pZFtncmVwbCgiUGxhc21hIiwgcG9pbnRfY29uZF91bWFwX2RmJGN0KV0gPSAiQiBjZWxscyIKCiMgYWRkIGxhYmVscyB0byBlZGdlcwptYXRjaF9kZiA9IHVuaXF1ZShkYXRhLmZyYW1lKCJjdCIgPSBwb2ludF9jb25kX3VtYXBfZGYkY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImN0X21pZCIgPSBwb2ludF9jb25kX3VtYXBfZGYkY3RfbWlkKSkKcm93bmFtZXMobWF0Y2hfZGYpID0gbWF0Y2hfZGYkY3QKCmVkZ2VfY29uZF91bWFwX2RmJG1pZF9nMSA9IG1hdGNoX2RmW2VkZ2VfY29uZF91bWFwX2RmJGN0X2cxLCJjdF9taWQiXQplZGdlX2NvbmRfdW1hcF9kZiRtaWRfZzIgPSBtYXRjaF9kZltlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMiwiY3RfbWlkIl0KCnBlX3VtYXBfbWlkX2wgPSBtYWtlTWVkaWFuKHBvaW50X2NvbmRfdW1hcF9kZiwgZWRnZV9jb25kX3VtYXBfZGYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBjbCA9IGMoImN0X21pZCIsICJtaWRfZzEiLCAibWlkX2cyIikpCgojIHBsb3QgdG90YWwgZ2VuZSBjb3JyZWxhdGlvbiBwcm9qZWN0aW9uIGFuZCBtZWRpYW4gbmV0d29yawpwbHRib3RoID0gZ2dwbG90KCkrCiAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfY29uZF91bWFwX2RmLCBtYXBwaW5nID0gYWVzKHggPSBYMSwgeSA9IFgyLCBjb2xvdXIgPSBjdDIpLCAKICAgICAgICAgICAgIGFscGhhID0gMC4yNSwgc2hvdy5sZWdlbmQgPSBGKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygwLDQsMTkpKSsKICBnZW9tX3NlZ21lbnQoZGF0YSA9IHBlX3VtYXBfbWlkX2xbWzJdXSwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnksIHNpemUgPSBGcmVxKSwgCiAgICAgICAgICAgICAgIGFscGhhID0gMC4xNSwgc2hvdy5sZWdlbmQgPSBGKSsKICBnZW9tX3BvaW50KGRhdGEgPSBwZV91bWFwX21pZF9sW1sxXV0sIAogICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0X21pZCksIAogICAgICAgICAgICAgYWxwaGEgPSAxLCBwY2ggPSAyMSwgc2l6ZSA9IDQpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgNCkpKwogIHRoZW1lX2NsYXNzaWMoKSsgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkKcHJpbnQocGx0Ym90aCkKCiMgZ2V0IG1lZGlhbiBwZXIgY2VsbCB0eXBlLCBwZXIgY29uZGl0aW9uIC0gSEVBTFRIWSBDT01QQVJJU09OIE5FVFdPUksKcGVfdW1hcF9jb25kX2xfbWlkID0gbWFrZU1lZGlhbkNvbmQocG9pbnRfY29uZF91bWFwX2RmLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZGdlX2NvbmRfdW1hcF9kZltlZGdlX2NvbmRfdW1hcF9kZiRpZF9jcF9pbnRlcmFjdGlvbiVpbiVjb21waF9pbnRlcnMsXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZGdlX2J5ID0gImNvbmQiLCBjbCA9IGMoImN0X21pZCIsICJtaWRfZzEiLCAibWlkX2cyIikpCgpwbHRfdW1hcF9jb25kX2xfY2ggPSBsaXN0KCkKZm9yKGNjIGluIHVuaXF1ZShwZV9jb25kX2xbWzJdXSRjb25kKSl7CiAgcGx0X3VtYXBfY29uZF9sX2NoW1tjY11dID0gZ2dwbG90KCkrCiAgICBnZW9tX3NlZ21lbnQoZGF0YSA9IHBlX3VtYXBfY29uZF9sX21pZFtbMl1dW3BlX3VtYXBfY29uZF9sX21pZFtbMl1dJGNvbmQ9PWNjLF0sIAogICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnksIHNpemUgPSBGcmVxLCBhbHBoYSA9IEZyZXEpKSsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHBlX3VtYXBfY29uZF9sX21pZFtbMV1dLCAKICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0X21pZCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpLCBsaW1pdHMgPSByYW5nZShwZV91bWFwX2NvbmRfbF9taWRbWzJdXSRGcmVxKSkrCiAgICBzY2FsZV9hbHBoYV9jb250aW51b3VzKGxpbWl0cyA9IHJhbmdlKHBlX3VtYXBfY29uZF9sX21pZFtbMl1dJEZyZXEpKSsKICAgIGxhYnModGl0bGUgPSBjYykrCiAgICBndWlkZXMoc2l6ZSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSwKICAgICAgICAgICBhbHBoYSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSkrCiAgICB0aGVtZV9jbGFzc2ljKCkKfQpwbHRfdW1hcF9jb25kX2xfY2hbWyJsZWciXV0gPSBjb3dwbG90OjpnZXRfbGVnZW5kKHBsdF91bWFwX2NvbmRfbF9jaFtbImhlYWx0aHkiXV0pCgojIHBsb3QgSEVBTFRIWSBDT01QQVJJU09OIE5FVFdPUksgbWVkaWFuIHBlciBjb25kaXRpb24KY293cGxvdDo6cGxvdF9ncmlkKHBsdF91bWFwX2NvbmRfbF9jaFtbMV1dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIAogICAgICAgICAgICAgICAgICAgcGx0X3VtYXBfY29uZF9sX2NoW1syXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwKICAgICAgICAgICAgICAgICAgIHBsdF91bWFwX2NvbmRfbF9jaFtbM11dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIHBsdF91bWFwX2NvbmRfbF9jaCRsZWcsIAogICAgICAgICAgICAgICAgICAgbmNvbCA9IDQsIHJlbF93aWR0aHMgPSBjKDEsMSwxLDAuNSkpCmBgYAoKU2F2ZSBVTUFQIG5ldHdvcmsgb2JqZWN0cwoKYGBge3J9CnNhdmUoZWRnZV9jb25kX3VtYXBfZGYsIHBvaW50X2NvbmRfdW1hcF9kZiwgCiAgICAgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS92My9uZXR3b3Jrc19jb25kX3VtYXAuUkRhdGEiKQpzYXZlKHBlX3VtYXBfbCwgcGVfdW1hcF9jb25kX2xfY2gsIAogICAgIGZpbGUgPSAicmVzdWx0cy9jZWxsX2NvbW0vdjMvbWVkaWFuX25ldHdvcmtzX2NvbmRfdW1hcC5SRGF0YSIpCnNhdmUocGVfdW1hcF9taWRfbCwgcGVfdW1hcF9jb25kX2xfbWlkLCAKICAgICBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3YzL21lZGlhbl9uZXR3b3Jrc19jb25kX3VtYXBfbWlkY3QuUkRhdGEiKQpgYGAKCgoK